Adds a view for displaying songs, as well as a basic song database API.
This commit is contained in:
@@ -9,4 +9,4 @@
|
|||||||
* For more details take a look at the Java Quickstart chapter in the Gradle
|
* For more details take a look at the Java Quickstart chapter in the Gradle
|
||||||
* user guide available at https://docs.gradle.org/4.4.1/userguide/tutorial_java_projects.html
|
* user guide available at https://docs.gradle.org/4.4.1/userguide/tutorial_java_projects.html
|
||||||
*/
|
*/
|
||||||
ext.defaultPackage = "edu.regis"
|
ext.defaultPackage = "edu.regis.universe_player"
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universe_player.data;
|
||||||
|
|
||||||
|
import java.awt.Image;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
|
public class Album implements Comparable<Album>
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
public String name;
|
||||||
|
public String[] artists;
|
||||||
|
public ImageIcon art;
|
||||||
|
public int year;
|
||||||
|
public String[] genres;
|
||||||
|
public int totalTracks;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Album o)
|
||||||
|
{
|
||||||
|
if (o != null)
|
||||||
|
{
|
||||||
|
return this.name.compareTo(o.name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,349 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universe_player.data;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A song provider that serves as a central point for any and all song providers, caching the results in memory for quick and easy access.
|
||||||
|
*
|
||||||
|
* @author William Hubbard
|
||||||
|
* @version 0.1
|
||||||
|
*/
|
||||||
|
public class CompiledSongProvider implements SongProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A set of all providers we pull from
|
||||||
|
*/
|
||||||
|
private HashMap<SongProvider, Set<Song>> providers = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of all albums used.
|
||||||
|
*/
|
||||||
|
private HashMap<Album, Set<Song>> cachedAlbums = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of all album names.
|
||||||
|
*/
|
||||||
|
private HashMap<String, Album> cachedAlbumNames = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of all songs used.
|
||||||
|
*/
|
||||||
|
private HashSet<Song> cachedSongs = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of all song artists used.
|
||||||
|
*/
|
||||||
|
private HashMap<String, Set<Song>> cachedArtists = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of all album artists used.
|
||||||
|
*/
|
||||||
|
private HashMap<String, Set<Album>> cachedAlbumArtists = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of all genres used.
|
||||||
|
*/
|
||||||
|
private HashMap<String, Set<Album>> cachedGenres = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of all years used.
|
||||||
|
*/
|
||||||
|
private HashMap<Integer, Set<Album>> cachedYears = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new CompiledSongProvider containing a set of existing providers.
|
||||||
|
*
|
||||||
|
* @param providers - Providers to add.
|
||||||
|
*/
|
||||||
|
public CompiledSongProvider(SongProvider... providers)
|
||||||
|
{
|
||||||
|
for (SongProvider provider : providers)
|
||||||
|
{
|
||||||
|
this.addProvider(provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a provider to the list
|
||||||
|
*
|
||||||
|
* @param provider - The provider to add.
|
||||||
|
*/
|
||||||
|
public void addProvider(SongProvider provider)
|
||||||
|
{
|
||||||
|
if (!this.providers.containsKey(provider))
|
||||||
|
{
|
||||||
|
this.providers.put(provider, new HashSet<>(provider.getSongs()));
|
||||||
|
this.addToCache(provider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches all the songs contained in a provider.
|
||||||
|
*
|
||||||
|
* @param provider - The provider to cache.
|
||||||
|
*/
|
||||||
|
private void addToCache(SongProvider provider)
|
||||||
|
{
|
||||||
|
for (Song song : provider.getSongs())
|
||||||
|
{
|
||||||
|
if (this.cachedSongs.add(song))
|
||||||
|
{
|
||||||
|
this.cachedAlbumNames.put(song.album.name, song.album);
|
||||||
|
if (!this.cachedAlbums.containsKey(song.album))
|
||||||
|
{
|
||||||
|
this.cachedAlbums.put(song.album, new HashSet<>());
|
||||||
|
}
|
||||||
|
this.cachedAlbums.get(song.album).add(song);
|
||||||
|
for (String artist : song.artists)
|
||||||
|
{
|
||||||
|
if (!this.cachedArtists.containsKey(artist))
|
||||||
|
{
|
||||||
|
this.cachedArtists.put(artist, new HashSet<>());
|
||||||
|
}
|
||||||
|
this.cachedArtists.get(artist).add(song);
|
||||||
|
}
|
||||||
|
for (String artist : song.album.artists)
|
||||||
|
{
|
||||||
|
if (!this.cachedAlbumArtists.containsKey(artist))
|
||||||
|
{
|
||||||
|
this.cachedAlbumArtists.put(artist, new HashSet<>());
|
||||||
|
}
|
||||||
|
this.cachedAlbumArtists.get(artist).add(song.album);
|
||||||
|
}
|
||||||
|
for (String genre : song.album.genres)
|
||||||
|
{
|
||||||
|
if (!this.cachedGenres.containsKey(genre))
|
||||||
|
{
|
||||||
|
this.cachedGenres.put(genre, new HashSet<>());
|
||||||
|
}
|
||||||
|
this.cachedGenres.get(genre).add(song.album);
|
||||||
|
}
|
||||||
|
if (!this.cachedYears.containsKey(song.album.year))
|
||||||
|
{
|
||||||
|
this.cachedYears.put(song.album.year, new HashSet<>());
|
||||||
|
}
|
||||||
|
this.cachedYears.get(song.album.year).add(song.album);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* If we couldn't add it, then another provider has provided that song already. We
|
||||||
|
* should remove it from our collection just to ensure that there is no confusion.
|
||||||
|
*/
|
||||||
|
this.providers.get(provider).remove(song);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a provider from the compilation.
|
||||||
|
*
|
||||||
|
* @param provider - The provider to remove.
|
||||||
|
*/
|
||||||
|
public void removeProvider(SongProvider provider)
|
||||||
|
{
|
||||||
|
this.removeFromCache(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all songs from a provider from a cache.
|
||||||
|
*
|
||||||
|
* @param provider - The provider to move out.
|
||||||
|
*/
|
||||||
|
private void removeFromCache(SongProvider provider)
|
||||||
|
{
|
||||||
|
Set<Song> songs = this.providers.remove(provider);
|
||||||
|
if (songs != null)
|
||||||
|
{
|
||||||
|
for (Song song : songs)
|
||||||
|
{
|
||||||
|
this.cachedSongs.remove(song);
|
||||||
|
this.cachedAlbums.get(song.album).remove(song);
|
||||||
|
/*
|
||||||
|
* Remove empty albums
|
||||||
|
*/
|
||||||
|
if (this.cachedAlbums.get(song.album).isEmpty())
|
||||||
|
{
|
||||||
|
this.cachedAlbums.remove(song.album);
|
||||||
|
this.cachedAlbumNames.remove(song.album.name);
|
||||||
|
}
|
||||||
|
for (String artist : song.artists)
|
||||||
|
{
|
||||||
|
this.cachedArtists.get(artist).remove(song);
|
||||||
|
if (this.cachedArtists.get(artist).isEmpty())
|
||||||
|
{
|
||||||
|
this.cachedArtists.remove(artist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String artist : song.album.artists)
|
||||||
|
{
|
||||||
|
this.cachedAlbumArtists.get(artist).remove(song.album);
|
||||||
|
if (this.cachedAlbumArtists.get(artist).isEmpty())
|
||||||
|
{
|
||||||
|
this.cachedAlbumArtists.remove(artist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String genre : song.album.genres)
|
||||||
|
{
|
||||||
|
this.cachedGenres.get(genre).remove(song.album);
|
||||||
|
if (this.cachedGenres.get(genre).isEmpty())
|
||||||
|
{
|
||||||
|
this.cachedGenres.remove(genre);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cachedYears.get(song.album.year).remove(song.album);
|
||||||
|
if (this.cachedYears.get(song.album.year).isEmpty())
|
||||||
|
{
|
||||||
|
this.cachedYears.remove(song.album.year);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums within the collection.
|
||||||
|
*
|
||||||
|
* @return A list of albums.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbums()
|
||||||
|
{
|
||||||
|
return this.cachedAlbums.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all songs within the collection.
|
||||||
|
*
|
||||||
|
* @return A list of songs.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Song> getSongs()
|
||||||
|
{
|
||||||
|
return this.cachedSongs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all artists.
|
||||||
|
*
|
||||||
|
* @return All artists.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<String> getArtists()
|
||||||
|
{
|
||||||
|
return this.cachedArtists.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all album artists.
|
||||||
|
*
|
||||||
|
* @return All album artists.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<String> getAlbumArtists()
|
||||||
|
{
|
||||||
|
return this.cachedAlbumArtists.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all genres.
|
||||||
|
*
|
||||||
|
* @return All genres.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<String> getGenres()
|
||||||
|
{
|
||||||
|
return this.cachedGenres.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all years that have albums.
|
||||||
|
*
|
||||||
|
* @return All years.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Integer> getYears()
|
||||||
|
{
|
||||||
|
return this.cachedYears.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all songs from an album.
|
||||||
|
*
|
||||||
|
* @param album - The album to obtain
|
||||||
|
* @return All songs from the requested album, or null if that album is not in the database.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Song> getSongsFromAlbum(Album album)
|
||||||
|
{
|
||||||
|
return this.cachedAlbums.get(album);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all songs written by a given artist.
|
||||||
|
*
|
||||||
|
* @param artist - The artist to search for
|
||||||
|
* @return A list of all songs from the specified artist, or null if that artist is not in the
|
||||||
|
* database.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Song> getSongsFromArtist(String artist)
|
||||||
|
{
|
||||||
|
return this.cachedArtists.get(artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains an album by a specific name.
|
||||||
|
*
|
||||||
|
* @param name - The name to search for.
|
||||||
|
* @return - The first album that matches the given name, or null if that album name is not in
|
||||||
|
* the database.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Album getAlbumByName(String name)
|
||||||
|
{
|
||||||
|
return this.cachedAlbumNames.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums that were written by a certain artist.
|
||||||
|
*
|
||||||
|
* @param artist - The artist to search for.
|
||||||
|
* @return - The collection on matching albums.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbumsFromArtist(String artist)
|
||||||
|
{
|
||||||
|
return this.cachedAlbumArtists.get(artist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums that match a certain genre
|
||||||
|
*
|
||||||
|
* @param genre - The genre to search for.
|
||||||
|
* @return - The collection on matching albums.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbumsFromGenre(String genre)
|
||||||
|
{
|
||||||
|
return this.cachedGenres.get(genre);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums that were released a certain year.
|
||||||
|
*
|
||||||
|
* @param year - The year to search for.
|
||||||
|
* @return - The collection on matching albums.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbumsFromYear(int year)
|
||||||
|
{
|
||||||
|
return this.cachedYears.get(year);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universe_player.data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A test song database that automatically generates a handful of songs.
|
||||||
|
*
|
||||||
|
* @author William Hubbard
|
||||||
|
* @version 0.1
|
||||||
|
*/
|
||||||
|
public class SimpleSongProvider implements SongProvider
|
||||||
|
{
|
||||||
|
private ArrayList<Album> albums;
|
||||||
|
private ArrayList<Song> songs;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbums()
|
||||||
|
{
|
||||||
|
if (albums == null)
|
||||||
|
{
|
||||||
|
albums = new ArrayList<>();
|
||||||
|
Random random = new Random();
|
||||||
|
StringBuilder builder;
|
||||||
|
String albumName, albumArtist;
|
||||||
|
Album album;
|
||||||
|
for (int albumNum = 0, numAlbums = random
|
||||||
|
.nextInt(5) + 5; albumNum < numAlbums; albumNum++)
|
||||||
|
{
|
||||||
|
builder = new StringBuilder();
|
||||||
|
for (int i = 0, l = random.nextInt(10) + 10; i < l; i++)
|
||||||
|
{
|
||||||
|
builder.append((char) (random.nextInt(26) + 97));
|
||||||
|
}
|
||||||
|
albumName = builder.toString();
|
||||||
|
|
||||||
|
builder = new StringBuilder();
|
||||||
|
for (int i = 0, l = random.nextInt(10) + 10; i < l; i++)
|
||||||
|
{
|
||||||
|
builder.append((char) (random.nextInt(26) + 97));
|
||||||
|
}
|
||||||
|
albumArtist = builder.toString();
|
||||||
|
|
||||||
|
album = new Album()
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
album.name = albumName;
|
||||||
|
album.artists = new String[]{albumArtist};
|
||||||
|
album.genres = new String[]{"Soundtrack"};
|
||||||
|
album.year = 2019;
|
||||||
|
album.totalTracks = random.nextInt(10) + 10;
|
||||||
|
album.id = albumNum;
|
||||||
|
|
||||||
|
albums.add(album);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return albums;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Song> getSongs()
|
||||||
|
{
|
||||||
|
if (songs == null)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Generate a list of songs
|
||||||
|
*/
|
||||||
|
Random random = new Random();
|
||||||
|
StringBuilder builder;
|
||||||
|
String songTitle;
|
||||||
|
Song song;
|
||||||
|
int songNum, numSongs;
|
||||||
|
songs = new ArrayList<>();
|
||||||
|
for (Album album : this.getAlbums())
|
||||||
|
{
|
||||||
|
for (songNum = 0, numSongs = album.totalTracks; songNum < numSongs; songNum++)
|
||||||
|
{
|
||||||
|
builder = new StringBuilder();
|
||||||
|
for (int i = 0, l = random.nextInt(10) + 10; i < l; i++)
|
||||||
|
{
|
||||||
|
builder.append((char) (random.nextInt(26) + 97));
|
||||||
|
}
|
||||||
|
songTitle = builder.toString();
|
||||||
|
|
||||||
|
song = new Song()
|
||||||
|
{
|
||||||
|
};
|
||||||
|
song.title = songTitle;
|
||||||
|
song.disc = 1;
|
||||||
|
song.trackNum = songNum + 1;
|
||||||
|
song.artists = album.artists.clone();
|
||||||
|
song.album = album;
|
||||||
|
songs.add(song);
|
||||||
|
}
|
||||||
|
album.totalTracks = numSongs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getArtists()
|
||||||
|
{
|
||||||
|
return this.songs.stream().flatMap(song -> Arrays.stream(song.artists)).sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getGenres()
|
||||||
|
{
|
||||||
|
return this.songs.stream().flatMap(song -> Arrays.stream(song.album.genres)).sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<String> getAlbumArtists()
|
||||||
|
{
|
||||||
|
return this.albums.stream().flatMap(album -> Arrays.stream(album.artists)).sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Integer> getYears()
|
||||||
|
{
|
||||||
|
return this.albums.stream().map(album -> album.year).sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Song> getSongsFromAlbum(Album album)
|
||||||
|
{
|
||||||
|
return this.songs.stream().filter(song -> song.album == album).sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Song> getSongsFromArtist(String artist)
|
||||||
|
{
|
||||||
|
return this.songs.stream().filter(song -> Arrays.asList(song.artists).contains(artist)).sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Album getAlbumByName(String name)
|
||||||
|
{
|
||||||
|
return this.albums.stream().filter(album -> album.name.equals(name)).findFirst().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbumsFromArtist(String artist)
|
||||||
|
{
|
||||||
|
return this.albums.stream().filter(album -> Arrays.asList(album.artists).contains(artist))
|
||||||
|
.sorted().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbumsFromGenre(String genre)
|
||||||
|
{
|
||||||
|
return this.albums.stream().filter(album -> Arrays.asList(album.genres).contains(genre))
|
||||||
|
.sorted().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Album> getAlbumsFromYear(int year)
|
||||||
|
{
|
||||||
|
return this.albums.stream().filter(album -> album.year == year)
|
||||||
|
.sorted().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universe_player.data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains data for a song.
|
||||||
|
*/
|
||||||
|
public abstract class Song implements Comparable<Song>
|
||||||
|
{
|
||||||
|
public String title;
|
||||||
|
public String[] artists;
|
||||||
|
public int trackNum;
|
||||||
|
public int disc;
|
||||||
|
/**
|
||||||
|
* A reference to the album this song is part of.
|
||||||
|
*/
|
||||||
|
public Album album;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Song o)
|
||||||
|
{
|
||||||
|
if (o != null)
|
||||||
|
{
|
||||||
|
int comp = this.album.compareTo(o.album);
|
||||||
|
if (comp == 0)
|
||||||
|
{
|
||||||
|
comp = Integer.compare(this.disc, o.disc);
|
||||||
|
if (comp == 0)
|
||||||
|
{
|
||||||
|
comp = Integer.compare(this.trackNum, o.trackNum);
|
||||||
|
if (comp == 0)
|
||||||
|
{
|
||||||
|
comp = this.title.compareTo(o.title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universe_player.data;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The song provider interface serves to give the application access to any form of song database as needed.
|
||||||
|
*
|
||||||
|
* @author William Hubbard
|
||||||
|
* @version 0.1
|
||||||
|
*/
|
||||||
|
public interface SongProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* A SongProvider instance designed to
|
||||||
|
*/
|
||||||
|
CompiledSongProvider INSTANCE = new CompiledSongProvider(new SimpleSongProvider());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums within the collection.
|
||||||
|
*
|
||||||
|
* @return A list of albums.
|
||||||
|
*/
|
||||||
|
Collection<Album> getAlbums();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all songs within the collection.
|
||||||
|
*
|
||||||
|
* @return A list of songs.
|
||||||
|
*/
|
||||||
|
Collection<Song> getSongs();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all artists.
|
||||||
|
*
|
||||||
|
* @return All artists.
|
||||||
|
*/
|
||||||
|
Collection<String> getArtists();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all album artists.
|
||||||
|
*
|
||||||
|
* @return All album artists.
|
||||||
|
*/
|
||||||
|
Collection<String> getAlbumArtists();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all genres.
|
||||||
|
*
|
||||||
|
* @return All genres.
|
||||||
|
*/
|
||||||
|
Collection<String> getGenres();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains a list of all years that have albums.
|
||||||
|
*
|
||||||
|
* @return All years.
|
||||||
|
*/
|
||||||
|
Collection<Integer> getYears();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all songs from an album.
|
||||||
|
*
|
||||||
|
* @param album - The album to obtain
|
||||||
|
* @return All songs from the requested album, or null if that album is not in the database.
|
||||||
|
*/
|
||||||
|
Collection<Song> getSongsFromAlbum(Album album);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all songs written by a given artist.
|
||||||
|
*
|
||||||
|
* @param artist - The artist to search for
|
||||||
|
* @return A list of all songs from the specified artist, or null if that artist is not in the
|
||||||
|
* database.
|
||||||
|
*/
|
||||||
|
Collection<Song> getSongsFromArtist(String artist);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains an album by a specific name.
|
||||||
|
*
|
||||||
|
* @param name - The name to search for.
|
||||||
|
* @return - The first album that matches the given name, or null if that album name is not in
|
||||||
|
* the database.
|
||||||
|
*/
|
||||||
|
Album getAlbumByName(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums that were written by a certain artist.
|
||||||
|
*
|
||||||
|
* @param artist - The artist to search for.
|
||||||
|
* @return - The collection on matching albums.
|
||||||
|
*/
|
||||||
|
Collection<Album> getAlbumsFromArtist(String artist);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums that match a certain genre
|
||||||
|
*
|
||||||
|
* @param genre - The genre to search for.
|
||||||
|
* @return - The collection on matching albums.
|
||||||
|
*/
|
||||||
|
Collection<Album> getAlbumsFromGenre(String genre);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains all albums that were released a certain year.
|
||||||
|
*
|
||||||
|
* @param year - The year to search for.
|
||||||
|
* @return - The collection on matching albums.
|
||||||
|
*/
|
||||||
|
Collection<Album> getAlbumsFromYear(int year);
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universe_player.player;
|
||||||
|
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SpringLayout;
|
||||||
|
|
||||||
|
import edu.regis.universe_player.data.Album;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This panel will display information on an album.
|
||||||
|
*/
|
||||||
|
public class AlbumInfo extends JPanel
|
||||||
|
{
|
||||||
|
private Album album;
|
||||||
|
private JLabel artLabel;
|
||||||
|
private JLabel albumName;
|
||||||
|
private JLabel artists;
|
||||||
|
private JLabel genres;
|
||||||
|
private JLabel year;
|
||||||
|
|
||||||
|
public AlbumInfo()
|
||||||
|
{
|
||||||
|
SpringLayout infoLayout = new SpringLayout();
|
||||||
|
this.setLayout(infoLayout);
|
||||||
|
|
||||||
|
this.artLabel = new JLabel();
|
||||||
|
this.add(this.artLabel);
|
||||||
|
this.albumName = new JLabel("Album");
|
||||||
|
this.add(this.albumName);
|
||||||
|
this.artists = new JLabel("Artists");
|
||||||
|
this.add(this.artists);
|
||||||
|
this.genres = new JLabel("Genres");
|
||||||
|
this.add(this.genres);
|
||||||
|
this.year = new JLabel("20XX");
|
||||||
|
this.add(this.year);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the layout information
|
||||||
|
*/
|
||||||
|
infoLayout.putConstraint(SpringLayout.NORTH, artLabel, 5, SpringLayout.NORTH, this);
|
||||||
|
infoLayout.putConstraint(SpringLayout.WEST, artLabel, 5, SpringLayout.WEST, this);
|
||||||
|
infoLayout.putConstraint(SpringLayout.NORTH, albumName, 5, SpringLayout.NORTH, this);
|
||||||
|
infoLayout.putConstraint(SpringLayout.WEST, albumName, 5, SpringLayout.EAST, artLabel);
|
||||||
|
infoLayout.putConstraint(SpringLayout.NORTH, artists, 5, SpringLayout.SOUTH, albumName);
|
||||||
|
infoLayout.putConstraint(SpringLayout.WEST, artists, 5, SpringLayout.EAST, artLabel);
|
||||||
|
infoLayout.putConstraint(SpringLayout.NORTH, genres, 5, SpringLayout.SOUTH, artists);
|
||||||
|
infoLayout.putConstraint(SpringLayout.WEST, genres, 5, SpringLayout.EAST, artLabel);
|
||||||
|
infoLayout.putConstraint(SpringLayout.NORTH, year, 5, SpringLayout.SOUTH, genres);
|
||||||
|
infoLayout.putConstraint(SpringLayout.WEST, year, 5, SpringLayout.EAST, artLabel);
|
||||||
|
// infoLayout.putConstraint(SpringLayout.EAST, this, 0, SpringLayout.EAST, albumName);
|
||||||
|
infoLayout.putConstraint(SpringLayout.EAST, this, 0, SpringLayout.EAST, artists);
|
||||||
|
// infoLayout.putConstraint(SpringLayout.EAST, this, 0, SpringLayout.EAST, genres);
|
||||||
|
// infoLayout.putConstraint(SpringLayout.EAST, this, 0, SpringLayout.EAST, year);
|
||||||
|
infoLayout.putConstraint(SpringLayout.SOUTH, this, 5, SpringLayout.EAST, artLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AlbumInfo(Album album)
|
||||||
|
{
|
||||||
|
this();
|
||||||
|
this.updateInfo(album);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateInfo(Album album)
|
||||||
|
{
|
||||||
|
final int ART_SIZE = 128;
|
||||||
|
ImageIcon icon;
|
||||||
|
StringBuilder builder;
|
||||||
|
|
||||||
|
this.album = album;
|
||||||
|
|
||||||
|
if (album.art != null)
|
||||||
|
{
|
||||||
|
icon = album.art;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
icon = new ImageIcon(this.getClass()
|
||||||
|
.getResource("/gui/icons/defaultart.png"), "Default");
|
||||||
|
}
|
||||||
|
icon.setImage(icon.getImage().getScaledInstance(ART_SIZE, ART_SIZE, 0));
|
||||||
|
this.artLabel.setIcon(icon);
|
||||||
|
|
||||||
|
this.albumName.setText(album.name);
|
||||||
|
|
||||||
|
builder = new StringBuilder();
|
||||||
|
if (album.artists != null && album.artists.length >= 1)
|
||||||
|
{
|
||||||
|
builder.append(album.artists[0]);
|
||||||
|
for (int i = 1, l = album.artists.length - 1; i < l; i++)
|
||||||
|
{
|
||||||
|
builder.append(", ");
|
||||||
|
builder.append(album.artists[i]);
|
||||||
|
}
|
||||||
|
if (album.artists.length > 1)
|
||||||
|
{
|
||||||
|
builder.append(" & ");
|
||||||
|
builder.append(album.artists[album.artists.length - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.artists.setText(builder.toString());
|
||||||
|
|
||||||
|
builder = new StringBuilder();
|
||||||
|
if (album.genres != null && album.genres.length >= 1)
|
||||||
|
{
|
||||||
|
builder.append(album.genres[0]);
|
||||||
|
for (int i = 1, l = album.genres.length - 1; i < l; i++)
|
||||||
|
{
|
||||||
|
builder.append(", ");
|
||||||
|
builder.append(album.genres[i]);
|
||||||
|
}
|
||||||
|
if (album.genres.length > 1)
|
||||||
|
{
|
||||||
|
builder.append(" & ");
|
||||||
|
builder.append(album.genres[album.genres.length - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.genres.setText(builder.toString());
|
||||||
|
|
||||||
|
this.year.setText(String.valueOf(album.year));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ package edu.regis.universe_player.player;
|
|||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
|
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Interface class serves as the primary GUI that the player interacts with.
|
* The Interface class serves as the primary GUI that the player interacts with.
|
||||||
@@ -29,6 +30,10 @@ public class Interface extends JFrame
|
|||||||
this.getContentPane().setLayout(new BorderLayout());
|
this.getContentPane().setLayout(new BorderLayout());
|
||||||
this.getContentPane().add(new Collections(), BorderLayout.LINE_START);
|
this.getContentPane().add(new Collections(), BorderLayout.LINE_START);
|
||||||
this.getContentPane().add(new PlayerControls(), BorderLayout.PAGE_END);
|
this.getContentPane().add(new PlayerControls(), BorderLayout.PAGE_END);
|
||||||
|
|
||||||
|
JScrollPane songList = new JScrollPane(new SongList());
|
||||||
|
this.getContentPane().add(songList, BorderLayout.CENTER);
|
||||||
|
|
||||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package edu.regis.universe_player.player;
|
||||||
|
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
import java.awt.GridBagConstraints;
|
||||||
|
import java.awt.GridBagLayout;
|
||||||
|
import java.awt.GridLayout;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SpringLayout;
|
||||||
|
|
||||||
|
import edu.regis.universe_player.data.Album;
|
||||||
|
import edu.regis.universe_player.data.SimpleSongProvider;
|
||||||
|
import edu.regis.universe_player.data.Song;
|
||||||
|
import edu.regis.universe_player.data.SongProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This panel will list all the songs that are to be currently displayed.
|
||||||
|
*/
|
||||||
|
public class SongList extends JPanel
|
||||||
|
{
|
||||||
|
private Map<Album, List<Song>> currentAlbums;
|
||||||
|
private Map<JLabel, Song> labelMap = new HashMap<>();
|
||||||
|
private Map<AlbumInfo, Album> artMap = new HashMap<>();
|
||||||
|
|
||||||
|
public SongList()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
GridBagLayout layout = new GridBagLayout();
|
||||||
|
this.setLayout(layout);
|
||||||
|
|
||||||
|
SongProvider provider = SongProvider.INSTANCE;
|
||||||
|
this.listAlbums(provider.getSongs());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the songs currently listed, sorted by album.
|
||||||
|
*
|
||||||
|
* @param songs - The songs to display.
|
||||||
|
*/
|
||||||
|
public void listAlbums(Collection<Song> songs)
|
||||||
|
{
|
||||||
|
Map<Album, List<Song>> albums = songs.stream().sorted().collect(Collectors
|
||||||
|
.groupingBy(song -> song.album, Collectors
|
||||||
|
.mapping(song -> song, Collectors.toList())));
|
||||||
|
GridBagConstraints c = new GridBagConstraints(), c2 = new GridBagConstraints();
|
||||||
|
c.fill = GridBagConstraints.NONE;
|
||||||
|
c.insets = new Insets(0, 0, 20, 0);
|
||||||
|
int i = 0, j;
|
||||||
|
AlbumInfo albumInfo;
|
||||||
|
List<Song> songCollection;
|
||||||
|
JPanel songList;
|
||||||
|
JLabel songNum, songTitle;
|
||||||
|
|
||||||
|
this.labelMap.clear();
|
||||||
|
this.artMap.clear();
|
||||||
|
this.removeAll();
|
||||||
|
this.currentAlbums = albums;
|
||||||
|
|
||||||
|
for (Album album : albums.keySet())
|
||||||
|
{
|
||||||
|
songCollection = albums.get(album);
|
||||||
|
|
||||||
|
albumInfo = new AlbumInfo(album);
|
||||||
|
c.gridx = 0;
|
||||||
|
c.gridy = i;
|
||||||
|
c.anchor = GridBagConstraints.WEST;
|
||||||
|
this.add(albumInfo, c);
|
||||||
|
this.artMap.put(albumInfo, album);
|
||||||
|
|
||||||
|
songList = new JPanel(new GridBagLayout());
|
||||||
|
j = 0;
|
||||||
|
for (Song song : songCollection)
|
||||||
|
{
|
||||||
|
songNum = new JLabel(String.valueOf(song.trackNum));
|
||||||
|
c2.gridx = 0;
|
||||||
|
c2.gridy = j;
|
||||||
|
c2.anchor = GridBagConstraints.EAST;
|
||||||
|
c2.insets = new Insets(0, 0, 0, 0);
|
||||||
|
songList.add(songNum, c2);
|
||||||
|
this.labelMap.put(songNum, song);
|
||||||
|
|
||||||
|
songTitle = new JLabel(song.title);
|
||||||
|
c2.gridx = 1;
|
||||||
|
c2.gridy = j;
|
||||||
|
c2.anchor = GridBagConstraints.WEST;
|
||||||
|
c2.insets = new Insets(0, 10, 0, 0);
|
||||||
|
songList.add(songTitle, c2);
|
||||||
|
this.labelMap.put(songTitle, song);
|
||||||
|
// TODO - Add song length or something
|
||||||
|
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
c.gridx = 1;
|
||||||
|
c.anchor = GridBagConstraints.WEST;
|
||||||
|
this.add(songList, c);
|
||||||
|
// this.add(new JLabel(new ImageIcon(this.getClass().getResource("/gui/icons/defaultart.png"), "Default")), c);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
interface/src/main/resources/gui/icons/defaultart.png
Executable file
BIN
interface/src/main/resources/gui/icons/defaultart.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 4.1 KiB |
Reference in New Issue
Block a user