/*
* Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner,
* Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain,
* Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter,
* Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann,
* Samuel Zweifel
*
* This file is part of Jukefox.
*
* Jukefox is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or any later version. Jukefox is
* distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Jukefox. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.ethz.dcg.jukefox.model.providers;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import ch.ethz.dcg.jukefox.commons.DataUnavailableException;
import ch.ethz.dcg.jukefox.commons.utils.MathUtils;
import ch.ethz.dcg.jukefox.commons.utils.Pair;
import ch.ethz.dcg.jukefox.commons.utils.RandomProvider;
import ch.ethz.dcg.jukefox.commons.utils.kdtree.KdTreePoint;
import ch.ethz.dcg.jukefox.data.cache.PreloadedData;
import ch.ethz.dcg.jukefox.data.cache.PreloadedDataManager;
import ch.ethz.dcg.jukefox.data.db.IDbDataPortal;
import ch.ethz.dcg.jukefox.model.collection.AlbumStatus;
import ch.ethz.dcg.jukefox.model.collection.BaseAlbum;
import ch.ethz.dcg.jukefox.model.collection.BaseArtist;
import ch.ethz.dcg.jukefox.model.collection.BaseSong;
import ch.ethz.dcg.jukefox.model.collection.CompleteTag;
import ch.ethz.dcg.jukefox.model.collection.Genre;
import ch.ethz.dcg.jukefox.model.collection.PlaylistSong;
import ch.ethz.dcg.jukefox.model.collection.PlaylistSong.SongSource;
import ch.ethz.dcg.jukefox.model.collection.SongCoords;
import ch.ethz.dcg.jukefox.model.collection.SongStatus;
import ch.ethz.dcg.jukefox.model.libraryimport.ImportSong;
import ch.ethz.dcg.jukefox.model.libraryimport.WebDataSong;
import edu.wlu.cs.levy.CG.KeySizeException;
/**
* Provides all possible data access options for song-objects
*/
public class SongProvider {
public static final int NUM_SONGS = 5;
private static final String TAG = SongProvider.class.getSimpleName();
private final IDbDataPortal dbDataPortal;
private final PreloadedDataManager preloadedDataManager;
/**
* Creates a new instance of {@link SongProvider}
*
* @param dbDataPortal
* The database data portal which will be used
* @param preloadedDataManager
* The preloaded data manager which will be used
*/
public SongProvider(IDbDataPortal dbDataPortal, PreloadedDataManager preloadedDataManager) {
this.dbDataPortal = dbDataPortal;
this.preloadedDataManager = preloadedDataManager;
}
// ----- BASE SONG -----
/**
* Gets a list of all available {@link BaseSong}
*
* @return A list of all available {@link BaseSong}
*/
public List<BaseSong<BaseArtist, BaseAlbum>> getAllBaseSongs() {
return dbDataPortal.getAllSongs();
}
/**
* Gets a list of {@link BaseSong} of the given {@link BaseArtist}
*
* @param baseArtist
* The {@link BaseArtist} of which you want generate the list of {@link BaseSong}
* @return A list of {@link BaseSong} of the given {@link BaseArtist}
*/
public List<BaseSong<BaseArtist, BaseAlbum>> getAllBaseSongs(BaseArtist baseArtist) {
return dbDataPortal.getSongsForArtist(baseArtist);
}
/**
* Gets a list of {@link BaseSong} of the given {@link Genre}
*
* @param genre
* The {@link Genre} of which you want generate the list of {@link BaseSong}
* @return A list of {@link BaseSong} of the given {@link Genre}
*/
public List<BaseSong<BaseArtist, BaseAlbum>> getAllBaseSongs(Genre genre) {
return dbDataPortal.getSongsForGenre(genre);
}
/**
* Gets a list of {@link BaseSong} searched by a search term
*
* @param searchTerm
* The search term ({@link String}) that describes the desired return value
* @param maxResults
* The maximum numbers ({@link Integer}) of results
* @return All results as a list of {@link BaseSong} of the given search terms
*/
public List<BaseSong<BaseArtist, BaseAlbum>> findBaseSongsBySearchString(String searchTerm, int maxResults) {
return dbDataPortal.findTitleBySearchString(searchTerm, maxResults);
}
/**
* Gets a list of {@link BaseSong} which are closest to a given position
*
* @param position
* The position (array of {@link Float}) of which the returned {@link BaseSong} should be closest to
* @param number
* Minimum number for the advanced kd tree algorithm
* @return A list of {@link BaseSong} which are closest to the given position
*/
public List<BaseSong<BaseArtist, BaseAlbum>> getClosestBaseSongsToPosition(float[] position, int number)
throws DataUnavailableException {
try {
Vector<KdTreePoint<Integer>> points = preloadedDataManager.getData().getSongsCloseToPosition(position,
number);
return dbDataPortal.getSongListForIds(points);
} catch (KeySizeException e) {
return new ArrayList<BaseSong<BaseArtist, BaseAlbum>>();
}
}
/**
* Gets a list of {@link BaseSong} which are closest to a given position
*
* @param position
* The position (array of {@link Float}) of which the returned {@link BaseSong} should be closest to
* @param number
* Minimum number for the advanced kd tree algorithm
* @return A list of Pairs of {@link BaseSong} and their positions which are closest to the given position
*/
public List<Pair<BaseSong<BaseArtist, BaseAlbum>, KdTreePoint<Integer>>> getClosestSongsToPosition2(
float[] position, int number) throws DataUnavailableException {
try {
Vector<KdTreePoint<Integer>> points = preloadedDataManager.getData().getSongsCloseToPosition(position,
number);
return dbDataPortal.getSongListForIds2(points);
} catch (KeySizeException e) {
return new ArrayList<Pair<BaseSong<BaseArtist, BaseAlbum>, KdTreePoint<Integer>>>();
}
}
/**
* @see PreloadedData#getSongsAroundPositionEuclidian(float[], float)
*/
public Vector<KdTreePoint<Integer>> getSongsAroundPositionEuclidian(float[] position, float distance)
throws DataUnavailableException {
try {
return preloadedDataManager.getData().getSongsAroundPositionEuclidian(position, distance);
} catch (KeySizeException e) {
throw new DataUnavailableException(e);
}
}
/**
* @see PreloadedData#getSongsAroundPositionHamming(float[], float)
*/
public Vector<KdTreePoint<Integer>> getSongsAroundPositionHamming(float[] position, float distance)
throws DataUnavailableException {
try {
return preloadedDataManager.getData().getSongsAroundPositionHamming(position, distance);
} catch (KeySizeException e) {
throw new DataUnavailableException(e);
}
}
/**
* Gets a {@link BaseSong} of the given song coordinates
*
* @param songCoords
* The song coordinates ({@link SongCoords}) of the returned {@link BaseSong}
* @return A {@link BaseSong} of the given song coordinates
*/
public BaseSong<BaseArtist, BaseAlbum> getBaseSong(SongCoords songCoords) throws DataUnavailableException {
return getBaseSong(songCoords.getId());
}
/**
* Gets a {@link BaseSong} of the given base song id
*
* @param id
* The base song id ({@link Integer}) of the returned {@link BaseSong}
* @return A {@link BaseSong} of the given base song id
*/
public BaseSong<BaseArtist, BaseAlbum> getBaseSong(int id) throws DataUnavailableException {
return dbDataPortal.getBaseSongById(id);
}
/**
* Gets a {@link BaseSong} of the given tag name
*
* @param tagName
* The tag name ({@link String}) of the returned {@link BaseSong}
* @return A {@link BaseSong} of the given tag name
*/
public BaseSong<BaseArtist, BaseAlbum> getBaseSong(String tagName) throws DataUnavailableException {
Integer tagId = dbDataPortal.getTagId(tagName, false);
CompleteTag completeTag = dbDataPortal.getCompleteTagById(tagId);
List<BaseSong<BaseArtist, BaseAlbum>> baseSongs = getClosestBaseSongsToPosition(MathUtils.normalizeCoordsSum(
completeTag.getPlsaCoords(), 1), NUM_SONGS);
BaseSong<BaseArtist, BaseAlbum> baseSong = baseSongs.get(RandomProvider.getRandom().nextInt(NUM_SONGS));
return baseSong;
}
/**
* Gets a list of {@link BaseSong} of the given {@link BaseAlbum}
*
* @param baseAlbum
* The {@link BaseAlbum} of the returned {@link BaseSong}
* @return A list of {@link BaseSong} of the given {@link BaseAlbum}
*/
public List<BaseSong<BaseArtist, BaseAlbum>> getAllBaseSongs(BaseAlbum baseAlbum) {
return dbDataPortal.getSongListForAlbum(baseAlbum);
}
/**
* Gets an arbitrary {@link BaseSong} in the given time range
*
* @param profileId
* The profile ID ({@link Integer})
* @param fromTimestamp
* Minimum timestamp ({@link Long}) of the returned {@link BaseSong}
* @param toTimestamp
* Maximum timestamp ({@link Long}) of the returned {@link BaseSong}
* @return An arbitrary {@link BaseSong} in the given time range
*/
public BaseSong<BaseArtist, BaseAlbum> getArbitraryBaseSongInTimeRange(int profileId, long fromTimestamp,
long toTimestamp) throws DataUnavailableException {
return dbDataPortal.getArbitrarySongInTimeRange(profileId, fromTimestamp, toTimestamp);
}
/**
* Gets the {@link BaseSong} of the given music explorer ID
*
* @param meId
* The music explorer ID ({@link Integer}) of the returned {@link BaseSong}
* @return The {@link BaseSong} of the given music explorer ID
*/
public BaseSong<BaseArtist, BaseAlbum> getBaseSongByMusicExplorerId(int meId) throws DataUnavailableException {
return dbDataPortal.getBaseSongByMusicExplorerId(meId);
}
/**
* Gets a {@link BaseSong} which is close to the given time range
*
* @param profileId
* The profile ID ({@link Integer})
* @param fromTimestamp
* Minimum timestamp ({@link Long}) of the returned {@link BaseSong}
* @param toTimestamp
* Maximum timestamp ({@link Long}) of the returned {@link BaseSong}
* @param toleranceRange
* Maximum time difference tolerance
* @param toleranceGlobal
* Global maximum time difference tolerance
* @return A {@link BaseSong} which is close to the given time range
*/
public BaseSong<BaseArtist, BaseAlbum> getBaseSongCloseToTimeRange(int profileId, long fromTimestamp,
long toTimestamp, float toleranceRange, float toleranceGlobal) throws DataUnavailableException {
return dbDataPortal.getSongCloseToTimeRange(profileId, fromTimestamp, toTimestamp, toleranceRange,
toleranceGlobal);
}
/**
* Gets a list of {@link BaseSong} for the given IDs
*
* @param points
* A {@link Vector} of points with the {@link BaseSong} IDs
* @return A list of {@link BaseSong} for the given IDs
*/
public List<BaseSong<BaseArtist, BaseAlbum>> getAllBaseSongsForIds(Vector<KdTreePoint<Integer>> points) {
return dbDataPortal.getSongListForIds(points);
}
/**
* Gets a list of {@link Pair} of {@link BaseSong} and {@link KdTreePoint} for the given IDs
*
* @param points
* A {@link Vector} of points with the {@link BaseSong} IDs
* @return A list of {@link Pair} of {@link BaseSong} and {@link KdTreePoint} for the given IDs
*/
public List<Pair<BaseSong<BaseArtist, BaseAlbum>, KdTreePoint<Integer>>> getAllBaseSongForIds2(
Vector<KdTreePoint<Integer>> points) {
return dbDataPortal.getSongListForIds2(points);
}
/**
* Get random songs for which there are music similarity coordinates
*
* @ param numberOfSongs the number of random songs that should be returned
*
* @return A random BaseSong from the collection
*/
public List<PlaylistSong<BaseArtist, BaseAlbum>> getRandomSongWithCoordinates(int numberOfSongs)
throws DataUnavailableException {
List<PlaylistSong<BaseArtist, BaseAlbum>> songs = new ArrayList<PlaylistSong<BaseArtist, BaseAlbum>>(
numberOfSongs);
List<Integer> ids = preloadedDataManager.getData().getIdsOfRandomSongsWithCoords(numberOfSongs);
for (Integer id : ids) {
songs
.add(new PlaylistSong<BaseArtist, BaseAlbum>(dbDataPortal.getBaseSongById(id),
SongSource.RANDOM_SONG));
}
return songs;
}
// ----- IMPORT SONG -----
/**
* Gets a {@link HashMap} of all available {@link ImportSong}
*
* @return Gets a {@link HashMap} of all available {@link ImportSong}
*/
public HashMap<String, ImportSong> getAllImportSongs() throws DataUnavailableException {
return dbDataPortal.getAllSongsForImport();
}
// ----- PLAYLIST SONG -----
/**
* Gets a list of all {@link PlaylistSong} for the given list of paths
*
* @param paths
* The {@link List} of paths ({@link String}) of the returned {@link PlaylistSong}
* @return A list of all {@link PlaylistSong} for the given list of paths
*/
public List<PlaylistSong<BaseArtist, BaseAlbum>> getAllPlaylistSongsForPaths(List<String> paths) {
return dbDataPortal.getSongListForPaths(paths);
}
/**
* Gets the {@link PlaylistSong} of the given path
*
* @param path
* The path ({@link String}) of the returned {@link PlaylistSong}
* @return The {@link PlaylistSong} of the given path
*/
public PlaylistSong<BaseArtist, BaseAlbum> getPlaylistSongForPath(String path) throws DataUnavailableException {
return dbDataPortal.getSongForPath(path);
}
/**
* Gets the {@link PlaylistSong} of the given path
*
* @param path
* The path ({@link String}) of the returned {@link PlaylistSong}
* @param caseSensitive
* Is the path case sensitive? ({@link Boolean})
* @return The {@link PlaylistSong} of the given path
*/
public PlaylistSong<BaseArtist, BaseAlbum> getSongForPath(String path, boolean caseSensitive)
throws DataUnavailableException {
return dbDataPortal.getSongForPath(path, caseSensitive);
}
/**
* Gets a list of all {@link PlaylistSong} for the given time range
*
* @param profileId
* The profile ID ({@link Integer})
* @param fromTimestamp
* Minimum timestamp ({@link Long}) of the returned {@link PlaylistSong}
* @param toTimestamp
* Maximum timestamp ({@link Long}) of the returned {@link PlaylistSong}
* @param maxResults
* The maximum numbers ({@link Integer}) of results
* @return A list of all {@link PlaylistSong} for the given time range
*/
public List<PlaylistSong<BaseArtist, BaseAlbum>> getPlaylistSongsForTimeRange(int profileId, long fromTimestamp,
long toTimestamp, int maxResults) {
return dbDataPortal.getSongsForTimeRange(profileId, fromTimestamp, toTimestamp, maxResults);
}
/**
* Returns a song that is picked from the collection uniformly at random
*
* @return
* @throws DataUnavailableException
* if no random song is available
*/
public PlaylistSong<BaseArtist, BaseAlbum> getRandomSong() throws DataUnavailableException {
return getRandomSongs(1).get(0);
}
/**
* Returns songs that are picked from the collection uniformly at random
*
* @param num
* How many songs should returned
* @return
* @throws DataUnavailableException
* if no random song is available
*/
public List<PlaylistSong<BaseArtist, BaseAlbum>> getRandomSongs(int num) throws DataUnavailableException {
num = Math.min(num, preloadedDataManager.getData().getNumberOfSongs());
if (num == 0) {
throw new DataUnavailableException("There are no songs in the collection");
}
// Find the random ids
Set<Integer> songIds = new HashSet<Integer>(num);
while (songIds.size() < num) {
songIds.add(preloadedDataManager.getData().getRandomSongId());
}
// Load the songs
List<PlaylistSong<BaseArtist, BaseAlbum>> ret = new ArrayList<PlaylistSong<BaseArtist, BaseAlbum>>(num);
for (Integer songId : songIds) {
ret.add(new PlaylistSong<BaseArtist, BaseAlbum>(dbDataPortal.getBaseSongById(songId),
SongSource.RANDOM_SONG));
}
return ret;
}
// ----- WEB DATA SONG -----
/**
* Gets a list of all {@link WebDataSong} for the given {@link SongStatus} and {@link AlbumStatus}
*
* @param songStatuses
* The {@link SongStatus} of the returned {@link WebDataSong}
* @param albumStatuses
* The {@link AlbumStatus} of the returned {@link WebDataSong}
* @return A list of all {@link WebDataSong} for the given {@link SongStatus} and {@link AlbumStatus}
*/
public List<WebDataSong> getWebDataSongsForStatus(SongStatus[] songStatuses, AlbumStatus[] albumStatuses) {
return dbDataPortal.getWebDataSongsForStatus(songStatuses, albumStatuses);
}
}