diff --git a/interface/src/main/java/edu/regis/universeplayer/data/InternetSongProvider.java b/interface/src/main/java/edu/regis/universeplayer/data/InternetSongProvider.java index c783e3e..e7108bc 100644 --- a/interface/src/main/java/edu/regis/universeplayer/data/InternetSongProvider.java +++ b/interface/src/main/java/edu/regis/universeplayer/data/InternetSongProvider.java @@ -16,6 +16,7 @@ import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; public class InternetSongProvider implements SongProvider @@ -32,6 +33,8 @@ public class InternetSongProvider implements SongProvider } return INSTANCE; } + + private AtomicBoolean updating = new AtomicBoolean(); private final HashMap songs = new HashMap<>(); private final HashMap albums = new HashMap<>(); @@ -63,6 +66,7 @@ public class InternetSongProvider implements SongProvider private void getSongCache() { + this.updating.set(true); service.submit(new SongQuery()); } @@ -74,6 +78,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getAlbums() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.albums.values(); } @@ -85,6 +103,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getSongs() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.songs) { return Collections.unmodifiableCollection(this.songs.values()); @@ -99,6 +131,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getArtists() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.artists; } @@ -110,6 +156,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getAlbumArtists() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.albumArtists; } @@ -121,6 +181,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getGenres() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.genres; } @@ -132,6 +206,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getYears() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.years; } @@ -144,6 +232,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getSongsFromAlbum(Album album) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.songs) { return this.songs.values().stream().filter(song -> song.album.equals(album)).collect(Collectors.toUnmodifiableSet()); @@ -160,6 +262,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getSongsFromArtist(String artist) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.songs) { return this.songs.values().stream().filter(song -> Arrays.asList(song.artists).contains(artist)).collect(Collectors.toUnmodifiableSet()); @@ -176,6 +292,20 @@ public class InternetSongProvider implements SongProvider @Override public Album getAlbumByName(String name) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.albums) { return this.albums.get(name); @@ -191,6 +321,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getAlbumsFromArtist(String artist) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.albums) { return this.albums.values().stream().filter(album -> Arrays.asList(album.artists).contains(artist)).collect(Collectors.toUnmodifiableSet()); @@ -206,6 +350,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getAlbumsFromGenre(String genre) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.albums) { return this.albums.values().stream().filter(album -> Arrays.asList(album.genres).contains(genre)).collect(Collectors.toUnmodifiableSet()); @@ -221,6 +379,20 @@ public class InternetSongProvider implements SongProvider @Override public Collection getAlbumsFromYear(int year) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.albums) { return this.albums.values().stream().filter(album -> album.year == year).collect(Collectors.toUnmodifiableSet()); @@ -340,128 +512,151 @@ public class InternetSongProvider implements SongProvider Album album; InternetSong song; int numAlbums = 0, numSongs = 0; - - try + + synchronized (updating) { - logger.debug("Querying database."); - /* - * Check if the table exists - */ - synchronized (DatabaseManager.getDb()) + updating.set(true); + try { + logger.debug("Querying database."); /* - * Make sure that a "null" album is available + * Check if the table exists */ - - if (albums.get(null) == null) + synchronized (DatabaseManager.getDb()) { - album = new Album(); - album.name = "Unknown"; - albums.put(null, album); - } - - if (albums.get("Unknown") == null) - { - albums.put("Unknown", albums.get(null)); - } - - state = DatabaseManager.getDb().createStatement(); - result = state.executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='INTERNET_ALBUMS';"); - if (!result.next()) - { - logger.debug("Creating album table."); /* - * Create the table + * Make sure that a "null" album is available */ - state.executeUpdate("CREATE TABLE INTERNET_ALBUMS" + - "(ALBUM TEXT PRIMARY KEY NOT NULL," + - "ARTISTS TEXT," + - "YEAR INTEGER," + - "GENRES TEXT," + - "TRACKS INTEGER," + - "DISCS INTEGER);"); - } - else - { - result = state.executeQuery("SELECT * FROM INTERNET_ALBUMS;"); - while (result.next()) + + if (albums.get(null) == null) { - album = albums.get(result.getString("album")); - if (album == null) - { - album = new Album(); - album.name = result.getString("album"); - albums.put(album.name, album); - } - album.artists = Optional.ofNullable(result.getString("artists")).map(s -> s.split(";")).orElse(new String[0]); - album.year = result.getInt("year"); - album.genres = Optional.ofNullable(result.getString("genres")).map(s -> s.split(";")).orElse(new String[0]); - album.totalTracks = result.getInt("tracks"); - album.totalDiscs = result.getInt("discs"); - numAlbums++; + album = new Album(); + album.name = "Unknown"; + albums.put(null, album); } - } - result = state.executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='INTERNET_SONGS';"); - if (!result.next()) - { - logger.debug("Creating song table."); - /* - * Create the table - */ - state.executeUpdate("CREATE TABLE INTERNET_SONGS" + - "(URL TEXT PRIMARY KEY NOT NULL," + - "TITLE TEXT," + - "ARTISTS TEXT," + - "TRACK INTEGER," + - "DISC INTEGER," + - "DURATION BIGINT," + - "ALBUM TEXT);"); - } - else - { - result = state.executeQuery("SELECT * FROM INTERNET_SONGS;"); - while (result.next()) + + if (albums.get("Unknown") == null) { - if (result.getString("url") == null) - { - continue; - } - URL url; - try - { - url = new URL(result.getString("url")); - } - catch (MalformedURLException e) - { - logger.error("Could not parse URL " + result.getString("url"), e); - continue; - } - song = songs.get(url); - if (song == null) - { - song = new InternetSong(); - song.location = url; - songs.put(song.location, song); - } - song.title = result.getString("title"); - song.artists = Optional.ofNullable(result.getString("artists")).map(s -> s.split(";")).orElse(new String[0]); - song.trackNum = result.getInt("track"); - song.disc = result.getInt("disc"); - song.duration = result.getLong("duration"); - song.album = Optional.ofNullable(result.getString("album")).map(albums::get).orElse(albums.get("Unknown")); - numSongs++; + albums.put("Unknown", albums.get(null)); } + + state = DatabaseManager.getDb().createStatement(); + result = state + .executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='INTERNET_ALBUMS';"); + if (!result.next()) + { + logger.debug("Creating album table."); + /* + * Create the table + */ + state.executeUpdate("CREATE TABLE INTERNET_ALBUMS" + + "(ALBUM TEXT PRIMARY KEY NOT NULL," + + "ARTISTS TEXT," + + "YEAR INTEGER," + + "GENRES TEXT," + + "TRACKS INTEGER," + + "DISCS INTEGER);"); + } + else + { + result = state + .executeQuery("SELECT * FROM INTERNET_ALBUMS;"); + while (result.next()) + { + album = albums.get(result.getString("album")); + if (album == null) + { + album = new Album(); + album.name = result.getString("album"); + albums.put(album.name, album); + } + album.artists = Optional + .ofNullable(result.getString("artists")) + .map(s -> s.split(";")) + .orElse(new String[0]); + album.year = result.getInt("year"); + album.genres = Optional + .ofNullable(result.getString("genres")) + .map(s -> s.split(";")) + .orElse(new String[0]); + album.totalTracks = result.getInt("tracks"); + album.totalDiscs = result.getInt("discs"); + numAlbums++; + } + } + result = state + .executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='INTERNET_SONGS';"); + if (!result.next()) + { + logger.debug("Creating song table."); + /* + * Create the table + */ + state.executeUpdate("CREATE TABLE INTERNET_SONGS" + + "(URL TEXT PRIMARY KEY NOT NULL," + + "TITLE TEXT," + + "ARTISTS TEXT," + + "TRACK INTEGER," + + "DISC INTEGER," + + "DURATION BIGINT," + + "ALBUM TEXT);"); + } + else + { + result = state + .executeQuery("SELECT * FROM INTERNET_SONGS;"); + while (result.next()) + { + if (result.getString("url") == null) + { + continue; + } + URL url; + try + { + url = new URL(result.getString("url")); + } + catch (MalformedURLException e) + { + logger.error("Could not parse URL " + result + .getString("url"), e); + continue; + } + song = songs.get(url); + if (song == null) + { + song = new InternetSong(); + song.location = url; + songs.put(song.location, song); + } + song.title = result.getString("title"); + song.artists = Optional + .ofNullable(result.getString("artists")) + .map(s -> s.split(";")) + .orElse(new String[0]); + song.trackNum = result.getInt("track"); + song.disc = result.getInt("disc"); + song.duration = result.getLong("duration"); + song.album = Optional + .ofNullable(result.getString("album")) + .map(albums::get) + .orElse(albums.get("Unknown")); + numSongs++; + } + } + state.close(); } - state.close(); } + catch (SQLException e) + { + logger.error("Could not query SQL database.", e); + } + logger.debug("Query complete, retrieved {} albums and {} songs", numAlbums, numSongs); + updatedSongs = 0; + totalUpdate = 0; + updating.set(false); + updating.notifyAll(); } - catch (SQLException e) - { - logger.error("Could not query SQL database.", e); - } - logger.debug("Query complete, retrieved {} albums and {} songs", numAlbums, numSongs); - updatedSongs = 0; - totalUpdate = 0; triggerUpdateListeners(); } } diff --git a/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java b/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java index 884ea7b..9d0e623 100644 --- a/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java +++ b/interface/src/main/java/edu/regis/universeplayer/data/LocalSongProvider.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.sql.*; import java.util.*; import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -28,6 +29,7 @@ public class LocalSongProvider implements SongProvider private final File source; + private final AtomicBoolean updating = new AtomicBoolean(false); private final HashMap songs = new HashMap<>(); private final HashMap albums = new HashMap<>(); /** @@ -174,6 +176,7 @@ public class LocalSongProvider implements SongProvider private void getSongCache() { + updating.set(true); service.submit(new SongQuery(true)); } @@ -185,6 +188,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getAlbums() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.albums.values(); } @@ -196,6 +213,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getSongs() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.songs) { return Collections.unmodifiableCollection(this.songs.values()); @@ -210,6 +241,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getArtists() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.artists; } @@ -221,6 +266,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getAlbumArtists() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.albumArtists; } @@ -232,6 +291,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getGenres() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.genres; } @@ -243,6 +316,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getYears() { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } return this.years; } @@ -256,6 +343,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getSongsFromAlbum(Album album) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.songs) { return this.songs.values().stream() @@ -274,6 +375,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getSongsFromArtist(String artist) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.songs) { return this.songs.values().stream() @@ -293,6 +408,20 @@ public class LocalSongProvider implements SongProvider @Override public Album getAlbumByName(String name) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.albums) { return this.albums.get(name); @@ -308,6 +437,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getAlbumsFromArtist(String artist) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.albums) { return this.albums.values().stream() @@ -326,6 +469,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getAlbumsFromGenre(String genre) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.albums) { return this.albums.values().stream() @@ -344,6 +501,20 @@ public class LocalSongProvider implements SongProvider @Override public Collection getAlbumsFromYear(int year) { + synchronized (this.updating) + { + while (this.updating.get()) + { + try + { + this.updating.wait(); + } + catch (InterruptedException e) + { + logger.error("Error while waiting for update lock", e); + } + } + } synchronized (this.albums) { return this.albums.values().stream() @@ -962,141 +1133,149 @@ public class LocalSongProvider implements SongProvider LocalSong song; int numAlbums = 0, numSongs = 0; - try + updating.set(true); + synchronized (updating) { - logger.debug("Querying database."); - /* - * Check if the table exists - */ - synchronized (DatabaseManager.getDb()) + try { + logger.debug("Querying database."); /* - * Make sure that a "null" album is available + * Check if the table exists */ - - if (albums.get(null) == null) + synchronized (DatabaseManager.getDb()) { - album = new Album(); - album.name = "Unknown"; - albums.put(null, album); - } - - if (albums.get("Unknown") == null) - { - albums.put("Unknown", albums.get(null)); - } - - state = DatabaseManager.getDb().createStatement(); - result = state - .executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='LOCAL_ALBUMS';"); - if (!result.next()) - { - logger.debug("Creating album table."); /* - * Create the table + * Make sure that a "null" album is available */ - state.executeUpdate("CREATE TABLE LOCAL_ALBUMS" + - "(ALBUM TEXT PRIMARY KEY NOT NULL," + - "ARTISTS TEXT," + - "YEAR INTEGER," + - "GENRES TEXT," + - "TRACKS INTEGER," + - "DISCS INTEGER);"); - } - else - { - result = state - .executeQuery("SELECT * FROM LOCAL_ALBUMS;"); - while (result.next()) + + if (albums.get(null) == null) { - album = albums.get(result.getString("album")); - if (album == null) - { - album = new Album(); - album.name = result.getString("album"); - albums.put(album.name, album); - } - album.artists = Optional - .ofNullable(result.getString("artists")) - .map(s -> s.split(";")) - .orElse(new String[0]); - album.year = result.getInt("year"); - album.genres = Optional - .ofNullable(result.getString("genres")) - .map(s -> s.split(";")) - .orElse(new String[0]); - album.totalTracks = result.getInt("tracks"); - album.totalDiscs = result.getInt("discs"); - numAlbums++; + album = new Album(); + album.name = "Unknown"; + albums.put(null, album); } - } - result = state - .executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='LOCAL_SONGS';"); - if (!result.next()) - { - logger.debug("Creating song table."); - /* - * Create the table - */ - state.executeUpdate("CREATE TABLE LOCAL_SONGS" + - "(FILE TEXT PRIMARY KEY NOT NULL," + - "CODEC CHAR(5)," + - "TYPE CHAR(5)," + - "TITLE TEXT," + - "ARTISTS TEXT," + - "TRACK INTEGER," + - "DISC INTEGER," + - "DURATION BIGINT," + - "ALBUM TEXT," + - "MOD BIGINT);"); - } - else - { - result = state - .executeQuery("SELECT * FROM LOCAL_SONGS;"); - while (result.next()) + + if (albums.get("Unknown") == null) { - if (result.getString("file") == null) - { - continue; - } - song = songs - .get(new File(result.getString("file"))); - if (song == null) - { - song = new LocalSong(); - song.file = new File(result.getString("file")); - songs.put(song.file, song); - } - song.codec = result.getString("codec"); - song.type = result.getString("type"); - song.title = result.getString("title"); - song.artists = Optional - .ofNullable(result.getString("artists")) - .map(s -> s.split(";")) - .orElse(new String[0]); - song.trackNum = result.getInt("track"); - song.disc = result.getInt("disc"); - song.duration = result.getLong("duration"); - song.album = Optional - .ofNullable(result.getString("album")) - .map(albums::get) - .orElse(albums.get("Unknown")); - numSongs++; + albums.put("Unknown", albums.get(null)); } + + state = DatabaseManager.getDb().createStatement(); + result = state + .executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='LOCAL_ALBUMS';"); + if (!result.next()) + { + logger.debug("Creating album table."); + /* + * Create the table + */ + state.executeUpdate("CREATE TABLE LOCAL_ALBUMS" + + "(ALBUM TEXT PRIMARY KEY NOT NULL," + + "ARTISTS TEXT," + + "YEAR INTEGER," + + "GENRES TEXT," + + "TRACKS INTEGER," + + "DISCS INTEGER);"); + } + else + { + result = state + .executeQuery("SELECT * FROM LOCAL_ALBUMS;"); + while (result.next()) + { + album = albums.get(result.getString("album")); + if (album == null) + { + album = new Album(); + album.name = result.getString("album"); + albums.put(album.name, album); + } + album.artists = Optional + .ofNullable(result.getString("artists")) + .map(s -> s.split(";")) + .orElse(new String[0]); + album.year = result.getInt("year"); + album.genres = Optional + .ofNullable(result.getString("genres")) + .map(s -> s.split(";")) + .orElse(new String[0]); + album.totalTracks = result.getInt("tracks"); + album.totalDiscs = result.getInt("discs"); + numAlbums++; + } + } + result = state + .executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='LOCAL_SONGS';"); + if (!result.next()) + { + logger.debug("Creating song table."); + /* + * Create the table + */ + state.executeUpdate("CREATE TABLE LOCAL_SONGS" + + "(FILE TEXT PRIMARY KEY NOT NULL," + + "CODEC CHAR(5)," + + "TYPE CHAR(5)," + + "TITLE TEXT," + + "ARTISTS TEXT," + + "TRACK INTEGER," + + "DISC INTEGER," + + "DURATION BIGINT," + + "ALBUM TEXT," + + "MOD BIGINT);"); + } + else + { + result = state + .executeQuery("SELECT * FROM LOCAL_SONGS;"); + while (result.next()) + { + if (result.getString("file") == null) + { + continue; + } + song = songs + .get(new File(result + .getString("file"))); + if (song == null) + { + song = new LocalSong(); + song.file = new File(result + .getString("file")); + songs.put(song.file, song); + } + song.codec = result.getString("codec"); + song.type = result.getString("type"); + song.title = result.getString("title"); + song.artists = Optional + .ofNullable(result.getString("artists")) + .map(s -> s.split(";")) + .orElse(new String[0]); + song.trackNum = result.getInt("track"); + song.disc = result.getInt("disc"); + song.duration = result.getLong("duration"); + song.album = Optional + .ofNullable(result.getString("album")) + .map(albums::get) + .orElse(albums.get("Unknown")); + numSongs++; + } + } + state.close(); } - state.close(); } - } - catch (SQLException e) - { - logger.error("Could not query SQL database.", e); - } - logger.debug("Query complete, retrieved {} albums and {} songs", numAlbums, numSongs); - updatedSongs = 0; - totalUpdate = 0; - triggerUpdateListeners(); + catch (SQLException e) + { + logger.error("Could not query SQL database.", e); + } + logger.debug("Query complete, retrieved {} albums and {} songs", numAlbums, numSongs); + updatedSongs = 0; + totalUpdate = 0; + updating.set(false); + updating.notifyAll(); + } + triggerUpdateListeners(); if (scan) { LinkedList scanners = new LinkedList<>(); diff --git a/interface/src/main/java/edu/regis/universeplayer/gui/Interface.java b/interface/src/main/java/edu/regis/universeplayer/gui/Interface.java index 7ccaa8e..ac606fc 100644 --- a/interface/src/main/java/edu/regis/universeplayer/gui/Interface.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/Interface.java @@ -102,7 +102,7 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL this.constructWindow(); this.setFocusManager(); - this.updateSongs(SongProvider.INSTANCE.getSongs()); +// this.updateSongs(SongProvider.INSTANCE.getSongs()); } protected void initActions() @@ -575,7 +575,6 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL { this.songList.listAlbums(songs); this.centerView.setViewportView(this.songList); - this.songList.revalidate(); this.centerView.revalidate(); } diff --git a/interface/src/main/java/edu/regis/universeplayer/gui/SongList.java b/interface/src/main/java/edu/regis/universeplayer/gui/SongList.java index 9b1ce79..c012225 100644 --- a/interface/src/main/java/edu/regis/universeplayer/gui/SongList.java +++ b/interface/src/main/java/edu/regis/universeplayer/gui/SongList.java @@ -82,211 +82,250 @@ public class SongList extends ScrollablePanel * * @param songs - The songs to display. */ - public void listAlbums(Collection songs) + public SwingWorker listAlbums(Collection songs) { - logger.debug("Sorting {} songs...", songs.size()); - Map> albums = songs.stream().sorted().collect(Collectors - .groupingBy(song -> song.album, Collectors - .mapping(song -> (Song) song, Collectors.toList()))); - logger.debug("Listing {} albums ({} songs)", - albums.size(), songs.size()); - GridBagConstraints c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - AtomicInteger i = new AtomicInteger(0); - - this.labelMap.clear(); - this.artMap.clear(); - this.removeAll(); - this.currentAlbums = albums; - - albums.keySet().stream().sorted().forEach((album) -> { - List songCollection = albums.get(album); - - AlbumInfo albumInfo = new AlbumInfo(album); - c.gridx = 0; - c.gridy = i.get(); - c.gridwidth = 1; - c.gridheight = songCollection.size(); - c.weightx = 0; - c.anchor = GridBagConstraints.NORTHWEST; - c.insets = new Insets(0, 0, 20, 10); - List finalSongCollection = songCollection; - albumInfo.addMouseListener((ClickListener) e -> { - if (e.getClickCount() == 2) - { - Queue.getInstance().addAll(finalSongCollection); - } - }); - albumInfo.albumName.addMouseListener((ClickListener) e -> { - if (e.getClickCount() == 2) - { - Container inter = this; - do - { - inter = inter.getParent(); - } - while (!(inter instanceof Interface) && inter - .getParent() != null); - if (inter instanceof Interface) - { - ((Interface) inter).updateSongs(SongProvider.INSTANCE - .getSongsFromAlbum(albumInfo.album)); - } - } - }); - albumInfo.artists.addMouseListener((ClickListener) e -> { - if (e.getClickCount() == 2) - { - Container inter = this; - do - { - inter = inter.getParent(); - } - while (!(inter instanceof Interface) && inter - .getParent() != null); - if (inter instanceof Interface) - { - ((Interface) inter) - .updateCollections(CollectionType.album, Arrays - .stream(albumInfo.album.artists) - .flatMap(s -> SongProvider.INSTANCE - .getAlbumsFromArtist(s) - .stream()) - .collect(Collectors.toList())); - } - } - }); - albumInfo.genres.addMouseListener((ClickListener) e -> { - if (e.getClickCount() == 2) - { - Container inter = this; - do - { - inter = inter.getParent(); - } - while (!(inter instanceof Interface) && inter - .getParent() != null); - if (inter instanceof Interface) - { - ((Interface) inter) - .updateCollections(CollectionType.album, Arrays - .stream(albumInfo.album.genres) - .flatMap(s -> SongProvider.INSTANCE - .getAlbumsFromGenre(s).stream()) - .collect(Collectors.toList())); - } - } - }); - albumInfo.year.addMouseListener((ClickListener) e -> { - if (e.getClickCount() == 2) - { - Container inter = this; - do - { - inter = inter.getParent(); - } - while (!(inter instanceof Interface) && inter - .getParent() != null); - if (inter instanceof Interface) - { - ((Interface) inter) - .updateCollections(CollectionType.album, SongProvider.INSTANCE - .getAlbumsFromYear(albumInfo.album.year)); - } - } - }); - this.add(albumInfo, c); - this.artMap.put(albumInfo, album); - - JButton firstSong = null; - - JLabel songNum; - JButton songTitle; - for (Song song : songCollection) + SwingWorker worker = new SwingWorker<>() + { + /** + * Computes a result, or throws an exception if unable + * to do so. + * + *

+ * Note that this method is executed only once. + * + *

+ * Note: this method is executed in a background + * thread. + * + * @return the computed result + * @throws Exception if unable to compute a result + */ + @Override + protected Object doInBackground() throws Exception { - songNum = new JLabel(String.valueOf(song.trackNum)); - songNum.setFocusable(false); - c.gridx = 1; - c.gridy = i.get(); - c.gridheight = 1; - c.weightx = 0; - c.anchor = GridBagConstraints.NORTHEAST; - c.insets = new Insets(0, 0, 0, 0); - this.add(songNum, c); - this.labelMap.put(songNum, song); + logger.debug("Sorting {} songs...", songs.size()); + Map> albums = songs.stream().sorted().collect(Collectors + .groupingBy(song -> song.album, Collectors + .mapping(song -> (Song) song, Collectors.toList()))); + logger.debug("Listing {} albums ({} songs)", + albums.size(), songs.size()); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + AtomicInteger i = new AtomicInteger(0); - songTitle = new JButton(song.title); - if (song.title == null || song.title.isEmpty()) - { - if (song instanceof LocalSong) - { - songTitle.setText(((LocalSong) song).file.getName()); - } - } - songTitle.setHorizontalAlignment(JButton.LEFT); - songTitle.setFocusPainted(true); - songTitle.setMargin(new Insets(0, 0, 0, 0)); - songTitle.setContentAreaFilled(false); - songTitle.setBorderPainted(false); - songTitle.setOpaque(false); - songTitle.addActionListener(new AbstractAction() - { - @Override - public void actionPerformed(ActionEvent e) - { - Queue.getInstance().add(song); - Queue.getInstance() - .skipToSong(Queue.getInstance().size() - 1); - } + SwingUtilities.invokeLater(() -> { + labelMap.clear(); + artMap.clear(); + removeAll(); + currentAlbums = albums; }); - c.gridx = 2; - c.gridy = i.get(); - c.weightx = 1.0; - c.anchor = GridBagConstraints.NORTHWEST; - c.insets = new Insets(0, 10, 0, 0); - this.add(songTitle, c); - this.labelMap.put(songTitle, song); - // TODO - Add song length or something - if (firstSong == null) - { - firstSong = songTitle; - JButton finalFirstSong = firstSong; - albumInfo.setAction(new AbstractAction() - { - @Override - public void actionPerformed(ActionEvent e) + LinkedHashMap albumInfos = + new LinkedHashMap<>(); + albums.keySet().stream().sorted().forEach((album) -> { + List songCollection = albums.get(album); + + AlbumInfo albumInfo = new AlbumInfo(album); + c.gridx = 0; + c.gridy = i.get(); + c.gridwidth = 1; + c.gridheight = songCollection.size(); + c.weightx = 0; + c.anchor = GridBagConstraints.NORTHWEST; + c.insets = new Insets(0, 0, 20, 10); + List finalSongCollection = songCollection; + albumInfo.addMouseListener((ClickListener) e -> { + if (e.getClickCount() == 2) { - finalFirstSong.requestFocusInWindow(); + Queue.getInstance().addAll(finalSongCollection); } }); - List finalSongCollection1 = songCollection; - albumInfo.addKeyListener(new KeyAdapter() - { - @Override - public void keyTyped(KeyEvent e) + albumInfo.albumName.addMouseListener((ClickListener) e -> { + if (e.getClickCount() == 2) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) + Container inter = SongList.this; + do { - Queue.getInstance() - .addAll(finalSongCollection1); + inter = inter.getParent(); + } + while (!(inter instanceof Interface) && inter + .getParent() != null); + if (inter instanceof Interface) + { + ((Interface) inter).updateSongs(SongProvider.INSTANCE + .getSongsFromAlbum(albumInfo.album)); } } }); - } - i.getAndIncrement(); - } + albumInfo.artists.addMouseListener((ClickListener) e -> { + if (e.getClickCount() == 2) + { + Container inter = SongList.this; + do + { + inter = inter.getParent(); + } + while (!(inter instanceof Interface) && inter + .getParent() != null); + if (inter instanceof Interface) + { + ((Interface) inter) + .updateCollections(CollectionType.album, Arrays + .stream(albumInfo.album.artists) + .flatMap(s -> SongProvider.INSTANCE + .getAlbumsFromArtist(s) + .stream()) + .collect(Collectors.toList())); + } + } + }); + albumInfo.genres.addMouseListener((ClickListener) e -> { + if (e.getClickCount() == 2) + { + Container inter = SongList.this; + do + { + inter = inter.getParent(); + } + while (!(inter instanceof Interface) && inter + .getParent() != null); + if (inter instanceof Interface) + { + ((Interface) inter) + .updateCollections(CollectionType.album, Arrays + .stream(albumInfo.album.genres) + .flatMap(s -> SongProvider.INSTANCE + .getAlbumsFromGenre(s).stream()) + .collect(Collectors.toList())); + } + } + }); + albumInfo.year.addMouseListener((ClickListener) e -> { + if (e.getClickCount() == 2) + { + Container inter = SongList.this; + do + { + inter = inter.getParent(); + } + while (!(inter instanceof Interface) && inter + .getParent() != null); + if (inter instanceof Interface) + { + ((Interface) inter) + .updateCollections(CollectionType.album, SongProvider.INSTANCE + .getAlbumsFromYear(albumInfo.album.year)); + } + } + }); + albumInfos.put(albumInfo, (GridBagConstraints) c.clone()); + artMap.put(albumInfo, album); + + JButton firstSong = null; + + JLabel songNum; + JButton songTitle; + AtomicInteger numSongs = new AtomicInteger(); + for (Song song : songCollection) + { + songNum = new JLabel(String.valueOf(song.trackNum)); + songNum.setFocusable(false); + c.gridx = 1; + c.gridy = i.get(); + c.gridheight = 1; + c.weightx = 0; + c.anchor = GridBagConstraints.NORTHEAST; + c.insets = new Insets(0, 0, 0, 0); + albumInfos.put(songNum, (GridBagConstraints) c.clone()); + labelMap.put(songNum, song); + + songTitle = new JButton(song.title); + if (song.title == null || song.title.isEmpty()) + { + if (song instanceof LocalSong) + { + songTitle.setText(((LocalSong) song).file.getName()); + } + } + songTitle.setHorizontalAlignment(JButton.LEFT); + songTitle.setFocusPainted(true); + songTitle.setMargin(new Insets(0, 0, 0, 0)); + songTitle.setContentAreaFilled(false); + songTitle.setBorderPainted(false); + songTitle.setOpaque(false); + songTitle.addActionListener(new AbstractAction() + { + @Override + public void actionPerformed(ActionEvent e) + { + Queue.getInstance().add(song); + Queue.getInstance() + .skipToSong(Queue.getInstance().size() - 1); + } + }); + c.gridx = 2; + c.gridy = i.get(); + c.weightx = 1.0; + c.anchor = GridBagConstraints.NORTHWEST; + c.insets = new Insets(0, 10, 0, 0); + albumInfos.put(songTitle, (GridBagConstraints) c.clone()); + labelMap.put(songTitle, song); + // TODO - Add song length or something + + if (firstSong == null) + { + firstSong = songTitle; + JButton finalFirstSong = firstSong; + albumInfo.setAction(new AbstractAction() + { + @Override + public void actionPerformed(ActionEvent e) + { + finalFirstSong.requestFocusInWindow(); + } + }); + List finalSongCollection1 = songCollection; + albumInfo.addKeyListener(new KeyAdapter() + { + @Override + public void keyTyped(KeyEvent e) + { + if (e.getKeyCode() == KeyEvent.VK_ENTER) + { + Queue.getInstance() + .addAll(finalSongCollection1); + } + } + }); + } + i.getAndIncrement(); + this.setProgress((int) (numSongs.incrementAndGet() / (float) songs.size() * 100F)); + } // this.add(new JLabel(new ImageIcon(this.getClass().getResource("/gui/icons/defaultart.png"), "Default")), c); - c.gridx = 0; - c.gridy = i.getAndIncrement(); - c.gridwidth = 3; - c.anchor = GridBagConstraints.NORTH; - this.add(new JSeparator(SwingConstants.HORIZONTAL), c); + c.gridx = 0; + c.gridy = i.getAndIncrement(); + c.gridwidth = 3; + c.anchor = GridBagConstraints.NORTH; + albumInfos.put(new JSeparator(SwingConstants.HORIZONTAL), + (GridBagConstraints) c.clone()); - i.getAndIncrement(); - }); - logger.debug("Song list built"); + i.getAndIncrement(); + }); + logger.debug("Song list built {} components", + albumInfos.size()); + SwingUtilities.invokeLater(() -> { + albumInfos.forEach((component, c1) -> { + add(component, c1); + }); + revalidate(); + logger.debug("Components added"); + }); + return null; + } + }; + worker.execute(); + return worker; } private class SongListPolicy extends FocusTraversalPolicy