Improves performance and accuracy of the song list.
Most of the problem was sorting stuff.
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
package edu.regis.universeplayer.data;
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
|
|
||||||
public class Album implements Comparable<Album>
|
public class Album implements Comparable<Album>
|
||||||
@@ -20,13 +22,22 @@ public class Album implements Comparable<Album>
|
|||||||
@Override
|
@Override
|
||||||
public int compareTo(Album o)
|
public int compareTo(Album o)
|
||||||
{
|
{
|
||||||
if (o != null)
|
if (o != null && o.name != null)
|
||||||
{
|
{
|
||||||
return this.name.compareTo(o.name);
|
return this.name.compareToIgnoreCase(o.name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Album{" +
|
||||||
|
"name='" + name + '\'' +
|
||||||
|
", artists=" + Arrays.toString(artists) +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,52 @@
|
|||||||
|
|
||||||
package edu.regis.universeplayer.data;
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class InternetSong extends Song
|
public class InternetSong extends Song
|
||||||
{
|
{
|
||||||
|
private static final Logger logger = LoggerFactory
|
||||||
|
.getLogger(InternetSong.class);
|
||||||
public URL location;
|
public URL location;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Song o)
|
||||||
|
{
|
||||||
|
int compare = super.compareTo(o);
|
||||||
|
if (compare == 0)
|
||||||
|
{
|
||||||
|
if (o instanceof LocalSong)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
compare =
|
||||||
|
this.location.toURI()
|
||||||
|
.compareTo(((InternetSong) o).location
|
||||||
|
.toURI());
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e)
|
||||||
|
{
|
||||||
|
logger.error("Could not compare locations {} and {}",
|
||||||
|
this.location, ((InternetSong) o).location, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Song{" +
|
||||||
|
"title='" + title + '\'' +
|
||||||
|
", artists=" + Arrays.toString(artists) +
|
||||||
|
", album=" + album +
|
||||||
|
", url=" + location +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package edu.regis.universeplayer.data;
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This song represents a song found on the local file system.
|
* This song represents a song found on the local file system.
|
||||||
@@ -14,4 +15,29 @@ public class LocalSong extends Song
|
|||||||
public File file;
|
public File file;
|
||||||
public String type;
|
public String type;
|
||||||
public String codec;
|
public String codec;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Song o)
|
||||||
|
{
|
||||||
|
int compare = super.compareTo(o);
|
||||||
|
if (compare == 0)
|
||||||
|
{
|
||||||
|
if (o instanceof LocalSong)
|
||||||
|
{
|
||||||
|
compare = this.file.compareTo(((LocalSong) o).file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return compare;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Song{" +
|
||||||
|
"title='" + title + '\'' +
|
||||||
|
", artists=" + Arrays.toString(artists) +
|
||||||
|
", album=" + album +
|
||||||
|
", url=" + file.getAbsolutePath() +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package edu.regis.universeplayer.data;
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains data for a song.
|
* Contains data for a song.
|
||||||
@@ -27,7 +28,9 @@ public class Song implements Comparable<Song>, Serializable
|
|||||||
{
|
{
|
||||||
if (o != null)
|
if (o != null)
|
||||||
{
|
{
|
||||||
int comp = this.album != null ? this.album.compareTo(o.album) : o.album != null ? 1 : 0;
|
int comp = this.album != null ?
|
||||||
|
(o.album != null ? this.album.compareTo(o.album) :
|
||||||
|
-1) : o.album != null ? 1 : 0;
|
||||||
if (comp == 0)
|
if (comp == 0)
|
||||||
{
|
{
|
||||||
comp = Integer.compare(this.disc, o.disc);
|
comp = Integer.compare(this.disc, o.disc);
|
||||||
@@ -36,7 +39,10 @@ public class Song implements Comparable<Song>, Serializable
|
|||||||
comp = Integer.compare(this.trackNum, o.trackNum);
|
comp = Integer.compare(this.trackNum, o.trackNum);
|
||||||
if (comp == 0)
|
if (comp == 0)
|
||||||
{
|
{
|
||||||
comp = this.title != null ? this.title.compareTo(o.title) : o.title != null ? 1 : 0;
|
comp = this.title != null ?
|
||||||
|
(o.title != null ?
|
||||||
|
this.title.compareTo(o.title) :
|
||||||
|
-1) : o.title != null ? 1 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,4 +53,14 @@ public class Song implements Comparable<Song>, Serializable
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "Song{" +
|
||||||
|
"title='" + title + '\'' +
|
||||||
|
", artists=" + Arrays.toString(artists) +
|
||||||
|
", album=" + album +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package edu.regis.universeplayer.data;
|
package edu.regis.universeplayer.data;
|
||||||
|
|
||||||
import edu.regis.universeplayer.player.Interface;
|
import edu.regis.universeplayer.player.Interface;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -19,10 +20,14 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
public class LocalSongProvider implements SongProvider<LocalSong>
|
public class LocalSongProvider implements SongProvider<LocalSong>
|
||||||
{
|
{
|
||||||
private static final Logger logger = LoggerFactory.getLogger(LocalSongProvider.class);
|
private static final Logger logger = LoggerFactory
|
||||||
|
.getLogger(LocalSongProvider.class);
|
||||||
private static final HashSet<String> formats = new HashSet<>();
|
private static final HashSet<String> formats = new HashSet<>();
|
||||||
private static final HashSet<String> codecs = new HashSet<>();
|
private static final HashSet<String> codecs = new HashSet<>();
|
||||||
|
|
||||||
|
private static final ForkJoinPool service = new ForkJoinPool();
|
||||||
|
private static String currentFolder;
|
||||||
|
|
||||||
private final File source;
|
private final File source;
|
||||||
|
|
||||||
private final HashMap<File, LocalSong> songs = new HashMap<>();
|
private final HashMap<File, LocalSong> songs = new HashMap<>();
|
||||||
@@ -56,8 +61,10 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
*/
|
*/
|
||||||
public static Set<String> getFormats()
|
public static Set<String> getFormats()
|
||||||
{
|
{
|
||||||
final Pattern FILEPAT = Pattern.compile("^\\s*[D ][E ]\\s*([a-z1-9_]{2,}(,[a-z_]{2,})*)\\s*[A-Za-z1-9 \\(\\)-/'\\.\":]+$");
|
final Pattern FILEPAT = Pattern
|
||||||
final Pattern FILEPAT2 = Pattern.compile("[a-z1-9_]{2,}(,[a-z1-9_]{2,})*");
|
.compile("^\\s*[D ][E ]\\s*([a-z1-9_]{2,}(,[a-z_]{2,})*)\\s*[A-Za-z1-9 \\(\\)-/'\\.\":]+$");
|
||||||
|
final Pattern FILEPAT2 = Pattern
|
||||||
|
.compile("[a-z1-9_]{2,}(,[a-z1-9_]{2,})*");
|
||||||
Matcher matcher;
|
Matcher matcher;
|
||||||
String ffmpegData;
|
String ffmpegData;
|
||||||
String name;
|
String name;
|
||||||
@@ -68,10 +75,12 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process process = Runtime.getRuntime().exec(new String[] {"ffmpeg", "-formats"});
|
Process process = Runtime.getRuntime()
|
||||||
|
.exec(new String[]{"ffmpeg", "-formats"});
|
||||||
logger.debug("Getting formats");
|
logger.debug("Getting formats");
|
||||||
logger.debug("Process complete");
|
logger.debug("Process complete");
|
||||||
try (Scanner scanner = new Scanner(process.getInputStream()))
|
try (Scanner scanner = new Scanner(process
|
||||||
|
.getInputStream()))
|
||||||
{
|
{
|
||||||
while (scanner.hasNextLine())
|
while (scanner.hasNextLine())
|
||||||
{
|
{
|
||||||
@@ -107,7 +116,8 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
*/
|
*/
|
||||||
public static Set<String> getCodecs()
|
public static Set<String> getCodecs()
|
||||||
{
|
{
|
||||||
final Pattern FILEPAT = Pattern.compile("^\\s*D[E.]A[I.][L.][S.]\\s*([a-z1-9_]{2,})\\s*.+$");
|
final Pattern FILEPAT = Pattern
|
||||||
|
.compile("^\\s*D[E.]A[I.][L.][S.]\\s*([a-z1-9_]{2,})\\s*.+$");
|
||||||
final Pattern FILEPAT2 = Pattern.compile("[a-z1-9_]{2,}");
|
final Pattern FILEPAT2 = Pattern.compile("[a-z1-9_]{2,}");
|
||||||
Matcher matcher;
|
Matcher matcher;
|
||||||
String ffmpegData;
|
String ffmpegData;
|
||||||
@@ -120,9 +130,11 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
logger.debug("Getting codecs");
|
logger.debug("Getting codecs");
|
||||||
Process process = Runtime.getRuntime().exec(new String[] {"ffmpeg", "-codecs"});
|
Process process = Runtime.getRuntime()
|
||||||
|
.exec(new String[]{"ffmpeg", "-codecs"});
|
||||||
logger.debug("Process complete");
|
logger.debug("Process complete");
|
||||||
try (Scanner scanner = new Scanner(process.getInputStream()))
|
try (Scanner scanner = new Scanner(process
|
||||||
|
.getInputStream()))
|
||||||
{
|
{
|
||||||
while (scanner.hasNextLine())
|
while (scanner.hasNextLine())
|
||||||
{
|
{
|
||||||
@@ -164,7 +176,7 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
|
|
||||||
private void getSongCache()
|
private void getSongCache()
|
||||||
{
|
{
|
||||||
SongScanner.service.submit(new SongQuery(true));
|
service.submit(new SongQuery(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,14 +252,17 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
* Obtains all songs from an album.
|
* Obtains all songs from an album.
|
||||||
*
|
*
|
||||||
* @param album - The album to obtain
|
* @param album - The album to obtain
|
||||||
* @return All songs from the requested album, or null if that album is not in the database.
|
* @return All songs from the requested album, or null if that album is not
|
||||||
|
* in the database.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Collection<LocalSong> getSongsFromAlbum(Album album)
|
public Collection<LocalSong> getSongsFromAlbum(Album album)
|
||||||
{
|
{
|
||||||
synchronized (this.songs)
|
synchronized (this.songs)
|
||||||
{
|
{
|
||||||
return this.songs.values().stream().filter(song -> song.album.equals(album)).collect(Collectors.toUnmodifiableSet());
|
return this.songs.values().stream()
|
||||||
|
.filter(song -> song.album.equals(album))
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,15 +270,18 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
* Obtains all songs written by a given artist.
|
* Obtains all songs written by a given artist.
|
||||||
*
|
*
|
||||||
* @param artist - The artist to search for
|
* @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
|
* @return A list of all songs from the specified artist, or null if that
|
||||||
* database.
|
* artist is not in the database.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Collection<LocalSong> getSongsFromArtist(String artist)
|
public Collection<LocalSong> getSongsFromArtist(String artist)
|
||||||
{
|
{
|
||||||
synchronized (this.songs)
|
synchronized (this.songs)
|
||||||
{
|
{
|
||||||
return this.songs.values().stream().filter(song -> Arrays.asList(song.artists).contains(artist)).collect(Collectors.toUnmodifiableSet());
|
return this.songs.values().stream()
|
||||||
|
.filter(song -> Arrays.asList(song.artists)
|
||||||
|
.contains(artist))
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,8 +289,8 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
* Obtains an album by a specific name.
|
* Obtains an album by a specific name.
|
||||||
*
|
*
|
||||||
* @param name - The name to search for.
|
* @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
|
* @return - The first album that matches the given name, or null if that
|
||||||
* the database.
|
* album name is not in the database.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Album getAlbumByName(String name)
|
public Album getAlbumByName(String name)
|
||||||
@@ -294,7 +312,10 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
{
|
{
|
||||||
synchronized (this.albums)
|
synchronized (this.albums)
|
||||||
{
|
{
|
||||||
return this.albums.values().stream().filter(album -> Arrays.asList(album.artists).contains(artist)).collect(Collectors.toUnmodifiableSet());
|
return this.albums.values().stream()
|
||||||
|
.filter(album -> Arrays.asList(album.artists)
|
||||||
|
.contains(artist))
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +330,10 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
{
|
{
|
||||||
synchronized (this.albums)
|
synchronized (this.albums)
|
||||||
{
|
{
|
||||||
return this.albums.values().stream().filter(album -> Arrays.asList(album.genres).contains(genre)).collect(Collectors.toUnmodifiableSet());
|
return this.albums.values().stream()
|
||||||
|
.filter(album -> Arrays.asList(album.genres)
|
||||||
|
.contains(genre))
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,7 +348,9 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
{
|
{
|
||||||
synchronized (this.albums)
|
synchronized (this.albums)
|
||||||
{
|
{
|
||||||
return this.albums.values().stream().filter(album -> album.year == year).collect(Collectors.toUnmodifiableSet());
|
return this.albums.values().stream()
|
||||||
|
.filter(album -> album.year == year)
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,7 +369,7 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
@Override
|
@Override
|
||||||
public String getUpdateText()
|
public String getUpdateText()
|
||||||
{
|
{
|
||||||
return SongScanner.currentFolder;
|
return currentFolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -360,14 +386,13 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
|
|
||||||
protected void triggerUpdateListeners()
|
protected void triggerUpdateListeners()
|
||||||
{
|
{
|
||||||
this.listeners.forEach(listener -> listener.onUpdate(this.getUpdateProgress(), this.getTotalUpdateSongs(), this.getUpdateText()));
|
this.listeners.forEach(listener -> listener
|
||||||
|
.onUpdate(this.getUpdateProgress(), this
|
||||||
|
.getTotalUpdateSongs(), this.getUpdateText()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SongScanner extends RecursiveAction
|
private class SongScanner extends RecursiveAction
|
||||||
{
|
{
|
||||||
private static final ForkJoinPool service = new ForkJoinPool();
|
|
||||||
private static String currentFolder;
|
|
||||||
|
|
||||||
private final File file;
|
private final File file;
|
||||||
|
|
||||||
SongScanner(File folder)
|
SongScanner(File folder)
|
||||||
@@ -399,20 +424,28 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (file.getName().lastIndexOf(".") < file.getName().length() - 1)
|
if (file.getName().lastIndexOf(".") < file.getName()
|
||||||
|
.length() - 1)
|
||||||
{
|
{
|
||||||
type = file.getName().substring(file.getName().lastIndexOf('.') + 1).toLowerCase();
|
type = file.getName()
|
||||||
|
.substring(file.getName().lastIndexOf('.') + 1)
|
||||||
|
.toLowerCase();
|
||||||
if (getFormats().contains(type))
|
if (getFormats().contains(type))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
synchronized (DatabaseManager.getDb())
|
synchronized (DatabaseManager.getDb())
|
||||||
{
|
{
|
||||||
state = DatabaseManager.getDb().createStatement();
|
state = DatabaseManager.getDb()
|
||||||
result = state.executeQuery("SELECT mod FROM local_songs WHERE file='" + this.file.getAbsolutePath().replaceAll("'", "''") + "';");
|
.createStatement();
|
||||||
|
result = state
|
||||||
|
.executeQuery("SELECT mod FROM local_songs WHERE file='" + this.file
|
||||||
|
.getAbsolutePath()
|
||||||
|
.replaceAll("'", "''") + "';");
|
||||||
if (result.next())
|
if (result.next())
|
||||||
{
|
{
|
||||||
if (result.getLong(1) >= this.file.lastModified())
|
if (result.getLong(1) >= this.file
|
||||||
|
.lastModified())
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* No modifications needed
|
* No modifications needed
|
||||||
@@ -424,21 +457,32 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
state.executeUpdate("UPDATE local_songs SET mod = " + this.file.lastModified() + " WHERE file='" + this.file.getAbsolutePath().replaceAll("'", "''") + "';");
|
state.executeUpdate("UPDATE local_songs SET mod = " + this.file
|
||||||
|
.lastModified() + " WHERE file='" + this.file
|
||||||
|
.getAbsolutePath()
|
||||||
|
.replaceAll("'", "''") + "';");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentFolder = file.getPath();
|
currentFolder = file.getPath();
|
||||||
codec = null;
|
codec = null;
|
||||||
process = Runtime.getRuntime().exec(new String[] {"ffprobe", "-hide_banner", file.getAbsolutePath()});
|
process = Runtime.getRuntime()
|
||||||
|
.exec(new String[]{"ffprobe", "-hide_banner", file.getAbsolutePath()});
|
||||||
process.waitFor();
|
process.waitFor();
|
||||||
try (Scanner scanner = new Scanner(process.getErrorStream()))
|
try (Scanner scanner = new Scanner(process
|
||||||
|
.getErrorStream()))
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (scanner.hasNextLine())
|
while (scanner.hasNextLine())
|
||||||
{
|
{
|
||||||
line = scanner.nextLine().trim();
|
line = scanner.nextLine().trim();
|
||||||
switch (line.toLowerCase().substring(0, line.indexOf(' ') > 0 ? line.indexOf(' ') : line.length()))
|
try
|
||||||
|
{
|
||||||
|
switch (line.toLowerCase()
|
||||||
|
.substring(0, line
|
||||||
|
.indexOf(' ') > 0 ? line
|
||||||
|
.indexOf(' ') : line
|
||||||
|
.length()))
|
||||||
{
|
{
|
||||||
case "genre" -> {
|
case "genre" -> {
|
||||||
/*
|
/*
|
||||||
@@ -448,44 +492,55 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
*/
|
*/
|
||||||
if (genre == null)
|
if (genre == null)
|
||||||
{
|
{
|
||||||
genre = line.substring(line.indexOf(':') + 2);
|
genre = line.substring(line
|
||||||
|
.indexOf(':') + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "title" -> {
|
case "title" -> {
|
||||||
if (title == null)
|
if (title == null)
|
||||||
{
|
{
|
||||||
title = line.substring(line.indexOf(':') + 2);
|
title = line.substring(line
|
||||||
|
.indexOf(':') + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "artist" -> {
|
case "artist" -> {
|
||||||
if (artist == null)
|
if (artist == null)
|
||||||
{
|
{
|
||||||
artist = line.substring(line.indexOf(':') + 2);
|
artist = line.substring(line
|
||||||
|
.indexOf(':') + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "album" -> {
|
case "album" -> {
|
||||||
if (albumTitle == null)
|
if (albumTitle == null)
|
||||||
{
|
{
|
||||||
albumTitle = line.substring(line.indexOf(':') + 2);
|
albumTitle = line.substring(line
|
||||||
|
.indexOf(':') + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "album_artist" -> {
|
case "album_artist" -> {
|
||||||
if (albumArtist == null)
|
if (albumArtist == null)
|
||||||
{
|
{
|
||||||
albumArtist = line.substring(line.indexOf(':') + 2);
|
albumArtist = line
|
||||||
|
.substring(line
|
||||||
|
.indexOf(':') + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "track" -> {
|
case "track" -> {
|
||||||
line = line.substring(line.indexOf(':') + 2);
|
line = line.substring(line
|
||||||
|
.indexOf(':') + 2);
|
||||||
if (line.indexOf('/') >= 0)
|
if (line.indexOf('/') >= 0)
|
||||||
{
|
{
|
||||||
track = Arrays.stream(line.split("/")).map(Integer::parseInt).toArray(Integer[]::new);
|
track = Arrays
|
||||||
|
.stream(line.split("/"))
|
||||||
|
.map(Integer::parseInt)
|
||||||
|
.toArray(Integer[]::new);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (track != null)
|
if (track != null)
|
||||||
{
|
{
|
||||||
track[0] = Integer.parseInt(line);
|
track[0] = Integer
|
||||||
|
.parseInt(line);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -496,24 +551,33 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
case "tracktotal" -> {
|
case "tracktotal" -> {
|
||||||
if (track != null)
|
if (track != null)
|
||||||
{
|
{
|
||||||
track[1] = Integer.parseInt(line.substring(line.indexOf(':') + 2));
|
track[1] = Integer.parseInt(line
|
||||||
|
.substring(line
|
||||||
|
.indexOf(':') + 2));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
track = new Integer[] {-1, Integer.parseInt(line.substring(line.indexOf(':') + 2))};
|
track = new Integer[]{-1, Integer.parseInt(line
|
||||||
|
.substring(line
|
||||||
|
.indexOf(':') + 2))};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "disc" -> {
|
case "disc" -> {
|
||||||
line = line.substring(line.indexOf(':') + 2);
|
line = line.substring(line
|
||||||
|
.indexOf(':') + 2);
|
||||||
if (line.indexOf('/') >= 0)
|
if (line.indexOf('/') >= 0)
|
||||||
{
|
{
|
||||||
disc = Arrays.stream(line.split("/")).map(Integer::parseInt).toArray(Integer[]::new);
|
disc = Arrays
|
||||||
|
.stream(line.split("/"))
|
||||||
|
.map(Integer::parseInt)
|
||||||
|
.toArray(Integer[]::new);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (disc != null)
|
if (disc != null)
|
||||||
{
|
{
|
||||||
disc[0] = Integer.parseInt(line);
|
disc[0] = Integer
|
||||||
|
.parseInt(line);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -524,18 +588,32 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
case "disctotal" -> {
|
case "disctotal" -> {
|
||||||
if (disc != null)
|
if (disc != null)
|
||||||
{
|
{
|
||||||
disc[1] = Integer.parseInt(line.substring(line.indexOf(':') + 2));
|
disc[1] = Integer.parseInt(line
|
||||||
|
.substring(line
|
||||||
|
.indexOf(':') + 2));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
disc = new Integer[] {-1, Integer.parseInt(line.substring(line.indexOf(':') + 2))};
|
disc = new Integer[]{-1, Integer.parseInt(line
|
||||||
|
.substring(line
|
||||||
|
.indexOf(':') + 2))};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "duration:" -> {
|
case "duration:" -> {
|
||||||
if (duration == 0)
|
if (duration == 0)
|
||||||
{
|
{
|
||||||
line = line.substring(line.indexOf(':') + 2, line.indexOf(','));
|
line = line.substring(line
|
||||||
duration = Long.parseLong(line.substring(0, 2)) * 3600 * 1000 + Long.parseLong(line.substring(3, 5)) * 60 * 1000 + Long.parseLong(line.substring(6, 8)) * 1000 + (long) (Float.parseFloat(line.substring(8, line.length() - 1)) * 1000);
|
.indexOf(':') + 2, line
|
||||||
|
.indexOf(','));
|
||||||
|
duration = Long.parseLong(line
|
||||||
|
.substring(0, 2)) * 3600 * 1000 + Long
|
||||||
|
.parseLong(line
|
||||||
|
.substring(3, 5)) * 60 * 1000 + Long
|
||||||
|
.parseLong(line
|
||||||
|
.substring(6, 8)) * 1000 + (long) (Float
|
||||||
|
.parseFloat(line
|
||||||
|
.substring(8, line
|
||||||
|
.length() - 1)) * 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "stream" -> {
|
case "stream" -> {
|
||||||
@@ -545,13 +623,16 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
codec = streamData[3];
|
codec = streamData[3];
|
||||||
if (codec.endsWith(","))
|
if (codec.endsWith(","))
|
||||||
{
|
{
|
||||||
codec = codec.substring(0, codec.length() - 1);
|
codec = codec
|
||||||
|
.substring(0, codec
|
||||||
|
.length() - 1);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* If this isn't a supported codec,
|
* If this isn't a supported codec,
|
||||||
* discard.
|
* discard.
|
||||||
*/
|
*/
|
||||||
if (!getCodecs().contains(codec))
|
if (!getCodecs()
|
||||||
|
.contains(codec))
|
||||||
{
|
{
|
||||||
logger.trace("Invalid codec {} for song {}", codec, file);
|
logger.trace("Invalid codec {} for song {}", codec, file);
|
||||||
codec = null;
|
codec = null;
|
||||||
@@ -568,6 +649,12 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (NumberFormatException e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Could not parse line \"" + line + "\"", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
// logger.trace("Finished scanning {}", file);
|
// logger.trace("Finished scanning {}", file);
|
||||||
}
|
}
|
||||||
if (codec != null)
|
if (codec != null)
|
||||||
@@ -580,29 +667,54 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
/*
|
/*
|
||||||
* This part in particular is prone to thread-safety issues.
|
* This part in particular is prone to thread-safety issues.
|
||||||
*/
|
*/
|
||||||
result = state.executeQuery("SELECT album FROM local_albums WHERE album='" + albumTitle + "';");
|
if (albumTitle != null)
|
||||||
|
{
|
||||||
|
albumTitle = albumTitle.replaceAll("'",
|
||||||
|
"''");
|
||||||
|
}
|
||||||
|
result = state
|
||||||
|
.executeQuery("SELECT album FROM local_albums WHERE album='" + albumTitle + "';");
|
||||||
if (!result.next())
|
if (!result.next())
|
||||||
{
|
{
|
||||||
state.executeUpdate("INSERT INTO local_albums (album) VALUES ('" + albumTitle + "');");
|
state.executeUpdate("INSERT INTO local_albums (album) VALUES ('" + albumTitle + "');");
|
||||||
}
|
}
|
||||||
result = state.executeQuery("SELECT artists FROM local_albums WHERE album='" + albumTitle + "';");
|
result = state
|
||||||
if (result.getString("artists") == null && albumArtist != null)
|
.executeQuery("SELECT artists FROM local_albums WHERE album='" + albumTitle + "';");
|
||||||
|
if (result
|
||||||
|
.getString("artists") == null && albumArtist != null)
|
||||||
{
|
{
|
||||||
state.executeUpdate("UPDATE local_albums SET artists='" + Arrays.stream(albumArtist.split(";")).map(String::trim).collect(Collectors.joining(";")) + "' WHERE album='" + albumTitle + "';");
|
state.executeUpdate("UPDATE " +
|
||||||
|
"local_albums SET artists='" + Arrays
|
||||||
|
.stream(albumArtist.split(";"))
|
||||||
|
.map(String::trim).map(s -> s
|
||||||
|
.replaceAll("'", "''"))
|
||||||
|
.collect(Collectors
|
||||||
|
.joining(";")) + "' WHERE album='" + albumTitle + "';");
|
||||||
}
|
}
|
||||||
// TODO - Can we get year metadata?
|
// TODO - Can we get year metadata?
|
||||||
result = state.executeQuery("SELECT genres FROM local_albums WHERE album='" + albumTitle + "';");
|
result = state
|
||||||
if (result.getString("genres") == null && genre != null)
|
.executeQuery("SELECT genres FROM local_albums WHERE album='" + albumTitle + "';");
|
||||||
|
if (result
|
||||||
|
.getString("genres") == null && genre != null)
|
||||||
{
|
{
|
||||||
state.executeUpdate("UPDATE local_albums SET genres='" + Arrays.stream(genre.split(";")).map(String::trim).collect(Collectors.joining(";")) + "' WHERE album='" + albumTitle + "';");
|
state.executeUpdate("UPDATE local_albums SET genres='" + Arrays
|
||||||
|
.stream(genre.split(";"))
|
||||||
|
.map(String::trim).map(s -> s
|
||||||
|
.replaceAll("'", "''"))
|
||||||
|
.collect(Collectors
|
||||||
|
.joining(";")) + "' WHERE album='" + albumTitle + "';");
|
||||||
}
|
}
|
||||||
result = state.executeQuery("SELECT tracks FROM local_albums WHERE album='" + albumTitle + "';");
|
result = state
|
||||||
if (result.getInt("tracks") == 0 && track != null && track[1] > 0)
|
.executeQuery("SELECT tracks FROM local_albums WHERE album='" + albumTitle + "';");
|
||||||
|
if (result
|
||||||
|
.getInt("tracks") == 0 && track != null && track[1] > 0)
|
||||||
{
|
{
|
||||||
state.executeUpdate("UPDATE local_albums SET tracks=" + track[1] + " WHERE album='" + albumTitle + "';");
|
state.executeUpdate("UPDATE local_albums SET tracks=" + track[1] + " WHERE album='" + albumTitle + "';");
|
||||||
}
|
}
|
||||||
result = state.executeQuery("SELECT discs FROM local_albums WHERE album='" + albumTitle + "';");
|
result = state
|
||||||
if (result.getInt("discs") == 0 && disc != null && disc[1] > 0)
|
.executeQuery("SELECT discs FROM local_albums WHERE album='" + albumTitle + "';");
|
||||||
|
if (result
|
||||||
|
.getInt("discs") == 0 && disc != null && disc[1] > 0)
|
||||||
{
|
{
|
||||||
state.executeUpdate("UPDATE local_albums SET tracks=" + disc[1] + " WHERE album='" + albumTitle + "';");
|
state.executeUpdate("UPDATE local_albums SET tracks=" + disc[1] + " WHERE album='" + albumTitle + "';");
|
||||||
}
|
}
|
||||||
@@ -610,24 +722,43 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
/*
|
/*
|
||||||
* Create the song
|
* Create the song
|
||||||
*/
|
*/
|
||||||
result = state.executeQuery("SELECT title FROM local_songs WHERE file='" + file.getAbsolutePath().replaceAll("'", "''") + "';");
|
result = state
|
||||||
|
.executeQuery("SELECT title FROM local_songs WHERE file='" + file
|
||||||
|
.getAbsolutePath()
|
||||||
|
.replaceAll("'", "''") + "';");
|
||||||
if (result.next())
|
if (result.next())
|
||||||
{
|
{
|
||||||
logger.debug("Updating song cache for {} ({})", title, file);
|
logger.debug("Updating song cache for {} ({})", title, file);
|
||||||
StringBuilder sql = new StringBuilder("UPDATE local_songs SET ");
|
StringBuilder sql = new StringBuilder("UPDATE local_songs SET ");
|
||||||
sql.append("codec='").append(codec).append("', ");
|
sql.append("codec='").append(codec)
|
||||||
sql.append("type='").append(codec).append("', ");
|
.append("', ");
|
||||||
|
sql.append("type='").append(codec)
|
||||||
|
.append("', ");
|
||||||
if (title != null && !title.isEmpty())
|
if (title != null && !title.isEmpty())
|
||||||
{
|
{
|
||||||
sql.append("title='").append(title.replaceAll("'", "''")).append("', ");
|
sql.append("title='").append(title
|
||||||
|
.replaceAll("'", "''"))
|
||||||
|
.append("', ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sql.append("title='").append(file.getName().replaceAll("'", "''")).append("', ");
|
sql.append("title='")
|
||||||
|
.append(file.getName()
|
||||||
|
.replaceAll("'", "''"))
|
||||||
|
.append("', ");
|
||||||
}
|
}
|
||||||
if (artist != null && !artist.isEmpty())
|
if (artist != null && !artist.isEmpty())
|
||||||
{
|
{
|
||||||
sql.append("artists='").append(Optional.of(artist).map(s -> s.split(";")).stream().flatMap(Arrays::stream).map(String::trim).collect(Collectors.joining(";"))).append("', ");
|
sql.append("artists='")
|
||||||
|
.append(Optional.of(artist)
|
||||||
|
.map(s -> s
|
||||||
|
.split(";"))
|
||||||
|
.stream()
|
||||||
|
.flatMap(Arrays::stream)
|
||||||
|
.map(String::trim)
|
||||||
|
.collect(Collectors
|
||||||
|
.joining(";")))
|
||||||
|
.append("', ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -635,7 +766,8 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
}
|
}
|
||||||
if (track != null && track[0] > 0)
|
if (track != null && track[0] > 0)
|
||||||
{
|
{
|
||||||
sql.append("track=").append(track[0]).append(", ");
|
sql.append("track=")
|
||||||
|
.append(track[0]).append(", ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -643,7 +775,8 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
}
|
}
|
||||||
if (disc != null && disc[0] > 0)
|
if (disc != null && disc[0] > 0)
|
||||||
{
|
{
|
||||||
sql.append("disc=").append(disc[0]).append(", ");
|
sql.append("disc=").append(disc[0])
|
||||||
|
.append(", ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -651,22 +784,30 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
}
|
}
|
||||||
if (duration != 0)
|
if (duration != 0)
|
||||||
{
|
{
|
||||||
sql.append("duration=").append(duration).append(", ");
|
sql.append("duration=")
|
||||||
|
.append(duration).append(", ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sql.append("duration=NULL, ");
|
sql.append("duration=NULL, ");
|
||||||
}
|
}
|
||||||
if (albumTitle != null && !albumTitle.isEmpty())
|
if (albumTitle != null && !albumTitle
|
||||||
|
.isEmpty())
|
||||||
{
|
{
|
||||||
sql.append("album='").append(albumTitle).append("', ");
|
sql.append("album='")
|
||||||
|
.append(albumTitle)
|
||||||
|
.append("', ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sql.append("album=NULL, ");
|
sql.append("album=NULL, ");
|
||||||
}
|
}
|
||||||
sql.append("mod=").append(file.lastModified());
|
sql.append("mod=")
|
||||||
sql.append(" WHERE file='").append(file.getAbsolutePath().replaceAll("'", "''")).append("';");
|
.append(file.lastModified());
|
||||||
|
sql.append(" WHERE file='")
|
||||||
|
.append(file.getAbsolutePath()
|
||||||
|
.replaceAll("'", "''"))
|
||||||
|
.append("';");
|
||||||
state.executeUpdate(sql.toString());
|
state.executeUpdate(sql.toString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -677,20 +818,36 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
StringBuilder values = new StringBuilder("(");
|
StringBuilder values = new StringBuilder("(");
|
||||||
|
|
||||||
columns.append("file,");
|
columns.append("file,");
|
||||||
values.append('\'').append(file.getAbsolutePath().replaceAll("'", "''")).append("',");
|
values.append('\'')
|
||||||
|
.append(file.getAbsolutePath()
|
||||||
|
.replaceAll("'", "''"))
|
||||||
|
.append("',");
|
||||||
columns.append("codec,");
|
columns.append("codec,");
|
||||||
values.append('\'').append(codec).append("',");
|
values.append('\'').append(codec)
|
||||||
|
.append("',");
|
||||||
columns.append("type,");
|
columns.append("type,");
|
||||||
values.append('\'').append(type).append("',");
|
values.append('\'').append(type)
|
||||||
|
.append("',");
|
||||||
if (title != null && !title.isEmpty())
|
if (title != null && !title.isEmpty())
|
||||||
{
|
{
|
||||||
columns.append("title,");
|
columns.append("title,");
|
||||||
values.append('\'').append(title.replaceAll("'", "''")).append("',");
|
values.append('\'').append(title
|
||||||
|
.replaceAll("'", "''"))
|
||||||
|
.append("',");
|
||||||
}
|
}
|
||||||
if (artist != null && !artist.isEmpty())
|
if (artist != null && !artist.isEmpty())
|
||||||
{
|
{
|
||||||
columns.append("artists,");
|
columns.append("artists,");
|
||||||
values.append('\'').append(Optional.of(artist).map(s -> s.split(";")).stream().flatMap(Arrays::stream).map(String::trim).collect(Collectors.joining(";"))).append("',");
|
values.append('\'')
|
||||||
|
.append(Optional.of(artist)
|
||||||
|
.map(s -> s
|
||||||
|
.split(";"))
|
||||||
|
.stream()
|
||||||
|
.flatMap(Arrays::stream)
|
||||||
|
.map(String::trim)
|
||||||
|
.collect(Collectors
|
||||||
|
.joining(";")))
|
||||||
|
.append("',");
|
||||||
}
|
}
|
||||||
if (track != null && track[0] > 0)
|
if (track != null && track[0] > 0)
|
||||||
{
|
{
|
||||||
@@ -707,10 +864,13 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
columns.append("duration,");
|
columns.append("duration,");
|
||||||
values.append(duration).append(",");
|
values.append(duration).append(",");
|
||||||
}
|
}
|
||||||
if (albumTitle != null && !albumTitle.isEmpty())
|
if (albumTitle != null && !albumTitle
|
||||||
|
.isEmpty())
|
||||||
{
|
{
|
||||||
columns.append("album,");
|
columns.append("album,");
|
||||||
values.append('\'').append(albumTitle).append("',");
|
values.append('\'')
|
||||||
|
.append(albumTitle)
|
||||||
|
.append("',");
|
||||||
}
|
}
|
||||||
|
|
||||||
columns.append("mod");
|
columns.append("mod");
|
||||||
@@ -820,7 +980,8 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
}
|
}
|
||||||
|
|
||||||
state = DatabaseManager.getDb().createStatement();
|
state = DatabaseManager.getDb().createStatement();
|
||||||
result = state.executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='LOCAL_ALBUMS';");
|
result = state
|
||||||
|
.executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='LOCAL_ALBUMS';");
|
||||||
if (!result.next())
|
if (!result.next())
|
||||||
{
|
{
|
||||||
logger.debug("Creating album table.");
|
logger.debug("Creating album table.");
|
||||||
@@ -837,7 +998,8 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = state.executeQuery("SELECT * FROM LOCAL_ALBUMS;");
|
result = state
|
||||||
|
.executeQuery("SELECT * FROM LOCAL_ALBUMS;");
|
||||||
while (result.next())
|
while (result.next())
|
||||||
{
|
{
|
||||||
album = albums.get(result.getString("album"));
|
album = albums.get(result.getString("album"));
|
||||||
@@ -847,15 +1009,22 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
album.name = result.getString("album");
|
album.name = result.getString("album");
|
||||||
albums.put(album.name, album);
|
albums.put(album.name, album);
|
||||||
}
|
}
|
||||||
album.artists = Optional.ofNullable(result.getString("artists")).map(s -> s.split(";")).orElse(new String[0]);
|
album.artists = Optional
|
||||||
|
.ofNullable(result.getString("artists"))
|
||||||
|
.map(s -> s.split(";"))
|
||||||
|
.orElse(new String[0]);
|
||||||
album.year = result.getInt("year");
|
album.year = result.getInt("year");
|
||||||
album.genres = Optional.ofNullable(result.getString("genres")).map(s -> s.split(";")).orElse(new String[0]);
|
album.genres = Optional
|
||||||
|
.ofNullable(result.getString("genres"))
|
||||||
|
.map(s -> s.split(";"))
|
||||||
|
.orElse(new String[0]);
|
||||||
album.totalTracks = result.getInt("tracks");
|
album.totalTracks = result.getInt("tracks");
|
||||||
album.totalDiscs = result.getInt("discs");
|
album.totalDiscs = result.getInt("discs");
|
||||||
numAlbums++;
|
numAlbums++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = state.executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='LOCAL_SONGS';");
|
result = state
|
||||||
|
.executeQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='LOCAL_SONGS';");
|
||||||
if (!result.next())
|
if (!result.next())
|
||||||
{
|
{
|
||||||
logger.debug("Creating song table.");
|
logger.debug("Creating song table.");
|
||||||
@@ -876,14 +1045,16 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = state.executeQuery("SELECT * FROM LOCAL_SONGS;");
|
result = state
|
||||||
|
.executeQuery("SELECT * FROM LOCAL_SONGS;");
|
||||||
while (result.next())
|
while (result.next())
|
||||||
{
|
{
|
||||||
if (result.getString("file") == null)
|
if (result.getString("file") == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
song = songs.get(new File(result.getString("file")));
|
song = songs
|
||||||
|
.get(new File(result.getString("file")));
|
||||||
if (song == null)
|
if (song == null)
|
||||||
{
|
{
|
||||||
song = new LocalSong();
|
song = new LocalSong();
|
||||||
@@ -893,11 +1064,17 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
song.codec = result.getString("codec");
|
song.codec = result.getString("codec");
|
||||||
song.type = result.getString("type");
|
song.type = result.getString("type");
|
||||||
song.title = result.getString("title");
|
song.title = result.getString("title");
|
||||||
song.artists = Optional.ofNullable(result.getString("artists")).map(s -> s.split(";")).orElse(new String[0]);
|
song.artists = Optional
|
||||||
|
.ofNullable(result.getString("artists"))
|
||||||
|
.map(s -> s.split(";"))
|
||||||
|
.orElse(new String[0]);
|
||||||
song.trackNum = result.getInt("track");
|
song.trackNum = result.getInt("track");
|
||||||
song.disc = result.getInt("disc");
|
song.disc = result.getInt("disc");
|
||||||
song.duration = result.getLong("duration");
|
song.duration = result.getLong("duration");
|
||||||
song.album = Optional.ofNullable(result.getString("album")).map(albums::get).orElse(albums.get("Unknown"));
|
song.album = Optional
|
||||||
|
.ofNullable(result.getString("album"))
|
||||||
|
.map(albums::get)
|
||||||
|
.orElse(albums.get("Unknown"));
|
||||||
numSongs++;
|
numSongs++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -934,7 +1111,8 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
{
|
{
|
||||||
if (dir.isDirectory())
|
if (dir.isDirectory())
|
||||||
{
|
{
|
||||||
for (File file : Objects.requireNonNullElse(dir.listFiles(), new File[0]))
|
for (File file : Objects
|
||||||
|
.requireNonNullElse(dir.listFiles(), new File[0]))
|
||||||
{
|
{
|
||||||
this.invokeFolder(file, scanners);
|
this.invokeFolder(file, scanners);
|
||||||
}
|
}
|
||||||
@@ -954,13 +1132,13 @@ public class LocalSongProvider implements SongProvider<LocalSong>
|
|||||||
{
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (SongScanner.service.awaitQuiescence(60, TimeUnit.SECONDS))
|
if (service.awaitQuiescence(60, TimeUnit.SECONDS))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.debug("Scan complete. Researching database");
|
logger.debug("Scan complete. Researching database");
|
||||||
SongScanner.currentFolder = "";
|
currentFolder = "";
|
||||||
updatedSongs = 0;
|
updatedSongs = 0;
|
||||||
totalUpdate = 0;
|
totalUpdate = 0;
|
||||||
triggerUpdateListeners();
|
triggerUpdateListeners();
|
||||||
|
|||||||
@@ -833,11 +833,13 @@ public class Interface extends JFrame implements SongDisplayListener, ComponentL
|
|||||||
this.controls.setUpdateProgress(updated, totalUpdate, updating);
|
this.controls.setUpdateProgress(updated, totalUpdate, updating);
|
||||||
if (updated == totalUpdate || totalUpdate == 0)
|
if (updated == totalUpdate || totalUpdate == 0)
|
||||||
{
|
{
|
||||||
logger.debug("Resetting the song provider.");
|
Collection<Song> songs = SongProvider.INSTANCE.getSongs();
|
||||||
|
logger.debug("Resetting the song provider with {} songs.",
|
||||||
|
songs.size());
|
||||||
/*
|
/*
|
||||||
* TODO - Add some way to get back to the current view, just updated
|
* TODO - Add some way to get back to the current view, just updated
|
||||||
*/
|
*/
|
||||||
this.updateSongs(SongProvider.INSTANCE.getSongs());
|
this.updateSongs(songs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,21 @@
|
|||||||
package edu.regis.universeplayer.player;
|
package edu.regis.universeplayer.player;
|
||||||
|
|
||||||
import com.wordpress.tips4java.ScrollablePanel;
|
import com.wordpress.tips4java.ScrollablePanel;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import edu.regis.universeplayer.ClickListener;
|
import edu.regis.universeplayer.ClickListener;
|
||||||
import edu.regis.universeplayer.data.Queue;
|
import edu.regis.universeplayer.data.Queue;
|
||||||
import edu.regis.universeplayer.data.*;
|
import edu.regis.universeplayer.data.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,6 +27,11 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
public class SongList extends ScrollablePanel
|
public class SongList extends ScrollablePanel
|
||||||
{
|
{
|
||||||
|
private static final Logger logger = LoggerFactory
|
||||||
|
.getLogger(SongList.class);
|
||||||
|
private static final ResourceBundle langs = ResourceBundle
|
||||||
|
.getBundle("lang.interface", Locale.getDefault());
|
||||||
|
|
||||||
private Map<Album, List<Song>> currentAlbums;
|
private Map<Album, List<Song>> currentAlbums;
|
||||||
private Map<JComponent, Song> labelMap = new HashMap<>();
|
private Map<JComponent, Song> labelMap = new HashMap<>();
|
||||||
private Map<AlbumInfo, Album> artMap = new HashMap<>();
|
private Map<AlbumInfo, Album> artMap = new HashMap<>();
|
||||||
@@ -45,7 +56,8 @@ public class SongList extends ScrollablePanel
|
|||||||
public void focusGained(FocusEvent e)
|
public void focusGained(FocusEvent e)
|
||||||
{
|
{
|
||||||
int index = -1;
|
int index = -1;
|
||||||
Component[] children = ((Container) e.getComponent()).getComponents();
|
Component[] children = ((Container) e.getComponent())
|
||||||
|
.getComponents();
|
||||||
for (int i = 0, l = children.length; index == -1 && i < l; i++)
|
for (int i = 0, l = children.length; index == -1 && i < l; i++)
|
||||||
{
|
{
|
||||||
if (children[i] == e.getOppositeComponent())
|
if (children[i] == e.getOppositeComponent())
|
||||||
@@ -55,7 +67,8 @@ public class SongList extends ScrollablePanel
|
|||||||
}
|
}
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
{
|
{
|
||||||
artMap.keySet().stream().findFirst().ifPresent(albumInfo -> {
|
artMap.keySet().stream().findFirst()
|
||||||
|
.ifPresent(albumInfo -> {
|
||||||
albumInfo.requestFocusInWindow();
|
albumInfo.requestFocusInWindow();
|
||||||
scrollRectToVisible(albumInfo.getBounds());
|
scrollRectToVisible(albumInfo.getBounds());
|
||||||
});
|
});
|
||||||
@@ -71,28 +84,27 @@ public class SongList extends ScrollablePanel
|
|||||||
*/
|
*/
|
||||||
public void listAlbums(Collection<? extends Song> songs)
|
public void listAlbums(Collection<? extends Song> songs)
|
||||||
{
|
{
|
||||||
|
logger.debug("Sorting {} songs...", songs.size());
|
||||||
Map<Album, List<Song>> albums = songs.stream().sorted().collect(Collectors
|
Map<Album, List<Song>> albums = songs.stream().sorted().collect(Collectors
|
||||||
.groupingBy(song -> song.album, Collectors
|
.groupingBy(song -> song.album, Collectors
|
||||||
.mapping(song -> (Song) song, Collectors.toList())));
|
.mapping(song -> (Song) song, Collectors.toList())));
|
||||||
|
logger.debug("Listing {} albums ({} songs)",
|
||||||
|
albums.size(), songs.size());
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
int i = 0;
|
AtomicInteger i = new AtomicInteger(0);
|
||||||
List<Song> songCollection;
|
|
||||||
JLabel songNum;
|
|
||||||
JButton songTitle;
|
|
||||||
|
|
||||||
this.labelMap.clear();
|
this.labelMap.clear();
|
||||||
this.artMap.clear();
|
this.artMap.clear();
|
||||||
this.removeAll();
|
this.removeAll();
|
||||||
this.currentAlbums = albums;
|
this.currentAlbums = albums;
|
||||||
|
|
||||||
for (Album album : albums.keySet())
|
albums.keySet().stream().sorted().forEach((album) -> {
|
||||||
{
|
List<Song> songCollection = albums.get(album);
|
||||||
songCollection = albums.get(album);
|
|
||||||
|
|
||||||
AlbumInfo albumInfo = new AlbumInfo(album);
|
AlbumInfo albumInfo = new AlbumInfo(album);
|
||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
c.gridy = i;
|
c.gridy = i.get();
|
||||||
c.gridwidth = 1;
|
c.gridwidth = 1;
|
||||||
c.gridheight = songCollection.size();
|
c.gridheight = songCollection.size();
|
||||||
c.weightx = 0;
|
c.weightx = 0;
|
||||||
@@ -113,10 +125,12 @@ public class SongList extends ScrollablePanel
|
|||||||
{
|
{
|
||||||
inter = inter.getParent();
|
inter = inter.getParent();
|
||||||
}
|
}
|
||||||
while (!(inter instanceof Interface) && inter.getParent() != null);
|
while (!(inter instanceof Interface) && inter
|
||||||
|
.getParent() != null);
|
||||||
if (inter instanceof Interface)
|
if (inter instanceof Interface)
|
||||||
{
|
{
|
||||||
((Interface) inter).updateSongs(SongProvider.INSTANCE.getSongsFromAlbum(albumInfo.album));
|
((Interface) inter).updateSongs(SongProvider.INSTANCE
|
||||||
|
.getSongsFromAlbum(albumInfo.album));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -128,10 +142,17 @@ public class SongList extends ScrollablePanel
|
|||||||
{
|
{
|
||||||
inter = inter.getParent();
|
inter = inter.getParent();
|
||||||
}
|
}
|
||||||
while (!(inter instanceof Interface) && inter.getParent() != null);
|
while (!(inter instanceof Interface) && inter
|
||||||
|
.getParent() != null);
|
||||||
if (inter instanceof Interface)
|
if (inter instanceof Interface)
|
||||||
{
|
{
|
||||||
((Interface) inter).updateCollections(CollectionType.album, Arrays.stream(albumInfo.album.artists).flatMap(s -> SongProvider.INSTANCE.getAlbumsFromArtist(s).stream()).collect(Collectors.toList()));
|
((Interface) inter)
|
||||||
|
.updateCollections(CollectionType.album, Arrays
|
||||||
|
.stream(albumInfo.album.artists)
|
||||||
|
.flatMap(s -> SongProvider.INSTANCE
|
||||||
|
.getAlbumsFromArtist(s)
|
||||||
|
.stream())
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -143,10 +164,16 @@ public class SongList extends ScrollablePanel
|
|||||||
{
|
{
|
||||||
inter = inter.getParent();
|
inter = inter.getParent();
|
||||||
}
|
}
|
||||||
while (!(inter instanceof Interface) && inter.getParent() != null);
|
while (!(inter instanceof Interface) && inter
|
||||||
|
.getParent() != null);
|
||||||
if (inter instanceof Interface)
|
if (inter instanceof Interface)
|
||||||
{
|
{
|
||||||
((Interface) inter).updateCollections(CollectionType.album, Arrays.stream(albumInfo.album.genres).flatMap(s -> SongProvider.INSTANCE.getAlbumsFromGenre(s).stream()).collect(Collectors.toList()));
|
((Interface) inter)
|
||||||
|
.updateCollections(CollectionType.album, Arrays
|
||||||
|
.stream(albumInfo.album.genres)
|
||||||
|
.flatMap(s -> SongProvider.INSTANCE
|
||||||
|
.getAlbumsFromGenre(s).stream())
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -158,10 +185,13 @@ public class SongList extends ScrollablePanel
|
|||||||
{
|
{
|
||||||
inter = inter.getParent();
|
inter = inter.getParent();
|
||||||
}
|
}
|
||||||
while (!(inter instanceof Interface) && inter.getParent() != null);
|
while (!(inter instanceof Interface) && inter
|
||||||
|
.getParent() != null);
|
||||||
if (inter instanceof Interface)
|
if (inter instanceof Interface)
|
||||||
{
|
{
|
||||||
((Interface) inter).updateCollections(CollectionType.album, SongProvider.INSTANCE.getAlbumsFromYear(albumInfo.album.year));
|
((Interface) inter)
|
||||||
|
.updateCollections(CollectionType.album, SongProvider.INSTANCE
|
||||||
|
.getAlbumsFromYear(albumInfo.album.year));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -170,12 +200,14 @@ public class SongList extends ScrollablePanel
|
|||||||
|
|
||||||
JButton firstSong = null;
|
JButton firstSong = null;
|
||||||
|
|
||||||
|
JLabel songNum;
|
||||||
|
JButton songTitle;
|
||||||
for (Song song : songCollection)
|
for (Song song : songCollection)
|
||||||
{
|
{
|
||||||
songNum = new JLabel(String.valueOf(song.trackNum));
|
songNum = new JLabel(String.valueOf(song.trackNum));
|
||||||
songNum.setFocusable(false);
|
songNum.setFocusable(false);
|
||||||
c.gridx = 1;
|
c.gridx = 1;
|
||||||
c.gridy = i;
|
c.gridy = i.get();
|
||||||
c.gridheight = 1;
|
c.gridheight = 1;
|
||||||
c.weightx = 0;
|
c.weightx = 0;
|
||||||
c.anchor = GridBagConstraints.NORTHEAST;
|
c.anchor = GridBagConstraints.NORTHEAST;
|
||||||
@@ -184,6 +216,13 @@ public class SongList extends ScrollablePanel
|
|||||||
this.labelMap.put(songNum, song);
|
this.labelMap.put(songNum, song);
|
||||||
|
|
||||||
songTitle = new JButton(song.title);
|
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.setHorizontalAlignment(JButton.LEFT);
|
||||||
songTitle.setFocusPainted(true);
|
songTitle.setFocusPainted(true);
|
||||||
songTitle.setMargin(new Insets(0, 0, 0, 0));
|
songTitle.setMargin(new Insets(0, 0, 0, 0));
|
||||||
@@ -196,11 +235,12 @@ public class SongList extends ScrollablePanel
|
|||||||
public void actionPerformed(ActionEvent e)
|
public void actionPerformed(ActionEvent e)
|
||||||
{
|
{
|
||||||
Queue.getInstance().add(song);
|
Queue.getInstance().add(song);
|
||||||
Queue.getInstance().skipToSong(Queue.getInstance().size() - 1);
|
Queue.getInstance()
|
||||||
|
.skipToSong(Queue.getInstance().size() - 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
c.gridx = 2;
|
c.gridx = 2;
|
||||||
c.gridy = i;
|
c.gridy = i.get();
|
||||||
c.weightx = 1.0;
|
c.weightx = 1.0;
|
||||||
c.anchor = GridBagConstraints.NORTHWEST;
|
c.anchor = GridBagConstraints.NORTHWEST;
|
||||||
c.insets = new Insets(0, 10, 0, 0);
|
c.insets = new Insets(0, 10, 0, 0);
|
||||||
@@ -228,23 +268,25 @@ public class SongList extends ScrollablePanel
|
|||||||
{
|
{
|
||||||
if (e.getKeyCode() == KeyEvent.VK_ENTER)
|
if (e.getKeyCode() == KeyEvent.VK_ENTER)
|
||||||
{
|
{
|
||||||
Queue.getInstance().addAll(finalSongCollection1);
|
Queue.getInstance()
|
||||||
|
.addAll(finalSongCollection1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
i++;
|
i.getAndIncrement();
|
||||||
}
|
}
|
||||||
// this.add(new JLabel(new ImageIcon(this.getClass().getResource("/gui/icons/defaultart.png"), "Default")), c);
|
// this.add(new JLabel(new ImageIcon(this.getClass().getResource("/gui/icons/defaultart.png"), "Default")), c);
|
||||||
|
|
||||||
c.gridx = 0;
|
c.gridx = 0;
|
||||||
c.gridy = i++;
|
c.gridy = i.getAndIncrement();
|
||||||
c.gridwidth = 3;
|
c.gridwidth = 3;
|
||||||
c.anchor = GridBagConstraints.NORTH;
|
c.anchor = GridBagConstraints.NORTH;
|
||||||
this.add(new JSeparator(SwingConstants.HORIZONTAL), c);
|
this.add(new JSeparator(SwingConstants.HORIZONTAL), c);
|
||||||
|
|
||||||
i++;
|
i.getAndIncrement();
|
||||||
}
|
});
|
||||||
|
logger.debug("Song list built");
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SongListPolicy extends FocusTraversalPolicy
|
private class SongListPolicy extends FocusTraversalPolicy
|
||||||
@@ -254,7 +296,8 @@ public class SongList extends ScrollablePanel
|
|||||||
{
|
{
|
||||||
Component[] children = aContainer.getComponents();
|
Component[] children = aContainer.getComponents();
|
||||||
int index = -1;
|
int index = -1;
|
||||||
for (int i = 0, l = aContainer.getComponentCount(); index == -1 && i < l; i++)
|
for (int i = 0, l = aContainer
|
||||||
|
.getComponentCount(); index == -1 && i < l; i++)
|
||||||
{
|
{
|
||||||
if (children[i] == aComponent)
|
if (children[i] == aComponent)
|
||||||
{
|
{
|
||||||
@@ -265,7 +308,8 @@ public class SongList extends ScrollablePanel
|
|||||||
{
|
{
|
||||||
if (aComponent instanceof AlbumInfo)
|
if (aComponent instanceof AlbumInfo)
|
||||||
{
|
{
|
||||||
for (int i = index + 1, l = aContainer.getComponentCount(); i < l; i++)
|
for (int i = index + 1, l = aContainer
|
||||||
|
.getComponentCount(); i < l; i++)
|
||||||
{
|
{
|
||||||
if (children[i] instanceof AlbumInfo)
|
if (children[i] instanceof AlbumInfo)
|
||||||
{
|
{
|
||||||
@@ -276,7 +320,8 @@ public class SongList extends ScrollablePanel
|
|||||||
}
|
}
|
||||||
else if (aComponent instanceof JButton)
|
else if (aComponent instanceof JButton)
|
||||||
{
|
{
|
||||||
for (int i = index + 1, l = aContainer.getComponentCount(); i < l; i++)
|
for (int i = index + 1, l = aContainer
|
||||||
|
.getComponentCount(); i < l; i++)
|
||||||
{
|
{
|
||||||
if (children[i].isFocusable())
|
if (children[i].isFocusable())
|
||||||
{
|
{
|
||||||
@@ -294,7 +339,8 @@ public class SongList extends ScrollablePanel
|
|||||||
{
|
{
|
||||||
Component[] children = aContainer.getComponents();
|
Component[] children = aContainer.getComponents();
|
||||||
int index = -1;
|
int index = -1;
|
||||||
for (int i = 0, l = aContainer.getComponentCount(); index == -1 && i < l; i++)
|
for (int i = 0, l = aContainer
|
||||||
|
.getComponentCount(); index == -1 && i < l; i++)
|
||||||
{
|
{
|
||||||
if (children[i] == aComponent)
|
if (children[i] == aComponent)
|
||||||
{
|
{
|
||||||
@@ -332,7 +378,9 @@ public class SongList extends ScrollablePanel
|
|||||||
@Override
|
@Override
|
||||||
public Component getFirstComponent(Container aContainer)
|
public Component getFirstComponent(Container aContainer)
|
||||||
{
|
{
|
||||||
Component comp = Arrays.stream(aContainer.getComponents()).filter(a -> a instanceof AlbumInfo).findFirst().orElse(null);
|
Component comp = Arrays.stream(aContainer.getComponents())
|
||||||
|
.filter(a -> a instanceof AlbumInfo)
|
||||||
|
.findFirst().orElse(null);
|
||||||
if (comp != null)
|
if (comp != null)
|
||||||
{
|
{
|
||||||
scrollRectToVisible(comp.getBounds());
|
scrollRectToVisible(comp.getBounds());
|
||||||
@@ -343,7 +391,9 @@ public class SongList extends ScrollablePanel
|
|||||||
@Override
|
@Override
|
||||||
public Component getLastComponent(Container aContainer)
|
public Component getLastComponent(Container aContainer)
|
||||||
{
|
{
|
||||||
Component[] matching = Arrays.stream(aContainer.getComponents()).filter(a -> a instanceof JButton).toArray(Component[]::new);
|
Component[] matching = Arrays.stream(aContainer.getComponents())
|
||||||
|
.filter(a -> a instanceof JButton)
|
||||||
|
.toArray(Component[]::new);
|
||||||
if (matching.length > 0)
|
if (matching.length > 0)
|
||||||
{
|
{
|
||||||
scrollRectToVisible(matching[matching.length - 1].getBounds());
|
scrollRectToVisible(matching[matching.length - 1].getBounds());
|
||||||
|
|||||||
Reference in New Issue
Block a user