/*
* Copyright (C) 2005-2009 Team XBMC
* http://xbmc.org
*
* This Program 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 2, or (at your option)
* any later version.
*
* This Program 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 XBMC Remote; see the file license. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
package org.xbmc.httpapi.client;
import java.util.ArrayList;
import java.util.HashMap;
import org.xbmc.api.business.INotifiableManager;
import org.xbmc.api.data.IControlClient;
import org.xbmc.api.data.IMusicClient;
import org.xbmc.api.data.IControlClient.ICurrentlyPlaying;
import org.xbmc.api.info.PlayStatus;
import org.xbmc.api.object.Album;
import org.xbmc.api.object.Artist;
import org.xbmc.api.object.Genre;
import org.xbmc.api.object.Host;
import org.xbmc.api.object.ICoverArt;
import org.xbmc.api.object.Song;
import org.xbmc.api.type.MediaType;
import org.xbmc.api.type.SortType;
import org.xbmc.httpapi.Connection;
import android.graphics.Bitmap;
/**
* Takes care of every music related stuff, notably the music database.
*
* @author Team XBMC
*/
public class MusicClient extends Client implements IMusicClient {
public static final String TAG = "MusicClient";
public static final int VIEW_ALBUMS = 1;
public static final int VIEW_SONGS = 2;
public static final String PLAYLIST_ID = "0";
public static final String LIBRARY_TYPE = "songs";
public static final int PLAYLIST_LIMIT = 100;
/**
* Class constructor needs reference to HTTP client connection
* @param connection
*/
public MusicClient(Connection connection) {
super(connection);
}
/**
* Updates host info on the connection.
* @param host
*/
public void setHost(Host host) {
mConnection.setHost(host);
}
/**
* Adds an album to the current playlist.
* @param album Album
* @return True on success, false otherwise.
*/
public boolean addToPlaylist(INotifiableManager manager, Album album, int sortBy, String sortOrder) {
return mConnection.getBoolean(manager, "AddToPlayListFromDB", LIBRARY_TYPE + ";" + getSongsCondition(album) + songsOrderBy(sortBy, sortOrder));
}
/**
* Adds all songs from an artist to the current playlist.
* @param artist Artist
* @return True on success, false otherwise.
*/
public boolean addToPlaylist(INotifiableManager manager, Artist artist, int sortBy, String sortOrder) {
return mConnection.getBoolean(manager, "AddToPlayListFromDB", LIBRARY_TYPE + ";" + getSongsCondition(artist) + songsOrderBy(sortBy, sortOrder));
}
/**
* Adds all songs from a genre to the current playlist.
* @param genre Genre
* @return True on success, false otherwise.
*/
public boolean addToPlaylist(INotifiableManager manager, Genre genre, int sortBy, String sortOrder) {
return mConnection.getBoolean(manager, "AddToPlayListFromDB", LIBRARY_TYPE + ";" + getSongsCondition(genre) + songsOrderBy(sortBy, sortOrder));
}
/**
* Adds songs of a genre from an artist to the current playlist.
* @param artist Artist
* @param genre Genre
* @return True on success, false otherwise.
*/
public boolean addToPlaylist(INotifiableManager manager, Artist artist, Genre genre, int sortBy, String sortOrder) {
return mConnection.getBoolean(manager, "AddToPlayListFromDB", LIBRARY_TYPE + ";" + getSongsCondition(artist, genre) + songsOrderBy(sortBy, sortOrder));
}
/**
* Adds a song to the current playlist.
* @param song Song to add
* @return True on success, false otherwise.
*/
public boolean addToPlaylist(INotifiableManager manager, Song song) {
return mConnection.getBoolean(manager, "AddToPlayList", song.path + ";" + PLAYLIST_ID);
}
/**
* Returns how many items are in the playlist.
* @return Number of items in the playlist
*/
public int getPlaylistSize(INotifiableManager manager) {
return mConnection.getInt(manager, "GetPlaylistLength", PLAYLIST_ID);
}
/**
* Retrieves the currently playing song number in the playlist.
* @return Number of items in the playlist
*/
public int getPlaylistPosition(INotifiableManager manager) {
return mConnection.getInt(manager, "GetPlaylistSong");
}
/**
* Sets the media at playlist position position to be the next item to be played.
* @param position New position, starting with 0.
* @return True on success, false otherwise.
*/
public boolean setPlaylistPosition(INotifiableManager manager, int position) {
return mConnection.getBoolean(manager, "SetPlaylistSong", String.valueOf(position));
}
/**
* Removes media from the current playlist. It is not possible to remove the media if it is currently being played.
* @param position Position to remove, starting with 0.
* @return True on success, false otherwise.
*/
public boolean removeFromPlaylist(INotifiableManager manager, int position) {
return mConnection.getBoolean(manager, "RemoveFromPlaylist", PLAYLIST_ID + ";" + position);
}
/**
* Removes media from the current playlist. It is not possible to remove the media if it is currently being played.
* @param position Complete path (including filename) of the media to be removed.
* @return True on success, false otherwise.
*/
public boolean removeFromPlaylist(INotifiableManager manager, String path) {
return mConnection.getBoolean(manager, "RemoveFromPlaylist", PLAYLIST_ID + ";" + path);
}
/**
* Returns the first {@link PLAYLIST_LIMIT} songs of the playlist.
* @return Songs in the playlist.
*/
public ArrayList<String> getPlaylist(INotifiableManager manager) {
ArrayList<String> temp= mConnection.getArray(manager, "GetPlaylistContents", PLAYLIST_ID);
return temp;
/*
final ArrayList<String> nodes = mConnection.getArray("GetDirectory", "playlistmusic://");
final ArrayList<String> ids = new ArrayList<String>();
final int playlistPosition = getPlaylistPosition();
int i = 0;
for (String node : nodes) {
ids.add(node.substring(node.lastIndexOf('/') + 1, node.lastIndexOf('.')));
if (++i > PLAYLIST_LIMIT + playlistPosition) {
break;
}
}
StringBuilder sql = new StringBuilder();
sql.append("idSong IN (");
int j = 0;
for (String id : ids) {
sql.append(id);
if (++j < i) {
sql.append(',');
}
}
sql.append(")");
final HashMap<Integer, Song> unsortedSongs = getSongsAsHashMap(sql);
final ArrayList<Song> sortedSongs = new ArrayList<Song>();
for (String node : nodes) {
try {
final int id = Integer.parseInt(node.substring(node.lastIndexOf('/') + 1, node.lastIndexOf('.')));
sortedSongs.add(unsortedSongs.get(id));
} catch (NumberFormatException e) {
Log.e(TAG, e.getMessage());
e.printStackTrace();
}
}
return sortedSongs;*/
}
/**
* Clears current playlist
* @return True on success, false otherwise.
*/
public boolean clearPlaylist(INotifiableManager manager) {
return mConnection.getBoolean(manager, "ClearPlayList", PLAYLIST_ID);
}
/**
* Adds a song to the current playlist and plays it.
* @param song Song
* @return True on success, false otherwise.
*/
public boolean play(INotifiableManager manager, Song song) {
return play(manager, getSongsCondition(song));
}
/**
* Plays an album. Playlist is previously cleared.
* @param album Album to play
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return True on success, false otherwise.
*/
public boolean play(INotifiableManager manager, Album album, int sortBy, String sortOrder) {
return play(manager, getSongsCondition(album).append(songsOrderBy(sortBy, sortOrder)));
}
/**
* Plays all songs of a genre. Playlist is previously cleared.
* @param genre Genre
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return True on success, false otherwise.
*/
public boolean play(INotifiableManager manager, Genre genre, int sortBy, String sortOrder) {
return play(manager, getSongsCondition(genre).append(songsOrderBy(sortBy, sortOrder)));
}
/**
* Plays all songs from an artist. Playlist is previously cleared.
* @param artist Artist
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return True on success, false otherwise.
*/
public boolean play(INotifiableManager manager, Artist artist, int sortBy, String sortOrder) {
return play(manager, getSongsCondition(artist).append(songsOrderBy(sortBy, sortOrder)));
}
/**
* Plays songs of a genre from an artist. Playlist is previously cleared.
* @param artist Artist
* @param genre Genre
* @return True on success, false otherwise.
*/
public boolean play(INotifiableManager manager, Artist artist, Genre genre) {
return play(manager, getSongsCondition(artist, genre).append(songsOrderBy(SortType.ARTIST, SortType.ORDER_ASC)));
}
/**
* Plays all songs fetched by a SQL condition.
* @param sqlCondition SQL Condition
* @return True on success, false otherwise.
*/
private boolean play(INotifiableManager manager, StringBuilder sqlCondition) {
clearPlaylist(manager);
mConnection.getBoolean(manager, "AddToPlayListFromDB", LIBRARY_TYPE + ";" + sqlCondition.toString());
mConnection.getBoolean(manager, "SetCurrentPlaylist", PLAYLIST_ID);
return playNext(manager);
}
/**
* Starts playing/showing the next media/image in the current playlist
* or, if currently showing a slidshow, the slideshow playlist.
* @return True on success, false otherwise.
*/
public boolean playNext(INotifiableManager manager) {
return mConnection.getBoolean(manager, "PlayNext");
}
/**
* Starts playing/showing the previous media/image in the current playlist
* or, if currently showing a slidshow, the slideshow playlist.
* @return True on success, false otherwise.
*/
public boolean playPrev(INotifiableManager manager) {
return mConnection.getBoolean(manager, "PlayPrev");
}
/**
* Sets the media at playlist position position to be the next item to be
* played. Position starts at 0, so SetPlaylistSong(5) sets the position
* to the 6th song in the playlist.
* @param pos Position
* @return true on success, false otherwise.
*/
public boolean playlistSetSong(INotifiableManager manager, int pos) {
return mConnection.getBoolean(manager, "SetPlaylistSong", String.valueOf(pos));
}
/**
* Gets all albums with given artist IDs
* @param artistIDs Array of artist IDs
* @return All compilation albums
*/
public ArrayList<Album> getAlbums(INotifiableManager manager, ArrayList<Integer> artistIDs) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT albumview.idAlbum, strAlbum, group_concat(DISTINCT strArtist) AS strArtists, iYear, art.url");
sb.append(" FROM albumview");
sb.append(" LEFT OUTER JOIN album_artist ON album_artist.idAlbum=albumview.idAlbum");
sb.append(" LEFT OUTER JOIN artist ON album_artist.idArtist=artist.idArtist");
sb.append(" LEFT OUTER JOIN art ON art.media_id=albumview.idAlbum AND art.media_type='album' AND art.type='thumb'");
sb.append(" WHERE albumview.strAlbum <> ''");
sb.append(" AND album_artist.idArtist IN (");
int n = 0;
for (Integer id : artistIDs) {
sb.append(id);
n++;
if (artistIDs.size() < n) {
sb.append(", ");
}
}
sb.append(") GROUP BY albumview.idAlbum");
return parseAlbums(mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Gets all albums from database
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return All albums
*/
public ArrayList<Album> getAlbums(INotifiableManager manager, int sortBy, String sortOrder) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT albumview.idAlbum, strAlbum, group_concat(DISTINCT strArtist) AS strArtists, iYear, art.url");
sb.append(" FROM albumview");
sb.append(" LEFT OUTER JOIN album_artist ON album_artist.idAlbum=albumview.idAlbum");
sb.append(" LEFT OUTER JOIN artist ON album_artist.idArtist=artist.idArtist");
sb.append(" LEFT OUTER JOIN art ON art.media_id=albumview.idAlbum AND art.media_type='album' AND art.type='thumb'");
sb.append(" WHERE albumview.strAlbum <> '' GROUP BY albumview.idAlbum");
sb.append(albumsOrderBy(sortBy, sortOrder));
return parseAlbums(mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Gets all albums of an artist from database
* @param artist Artist
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return Albums with an artist
*/
public ArrayList<Album> getAlbums(INotifiableManager manager, Artist artist, int sortBy, String sortOrder) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT albumview.idAlbum, strAlbum, group_concat(DISTINCT strArtist) AS strArtists, iYear, art.url");
sb.append(" FROM albumview");
sb.append(" LEFT OUTER JOIN album_artist ON album_artist.idAlbum=albumview.idAlbum");
sb.append(" LEFT OUTER JOIN artist ON album_artist.idArtist=artist.idArtist");
sb.append(" LEFT OUTER JOIN art ON art.media_id=albumview.idAlbum AND art.media_type='album' AND art.type='thumb'");
sb.append(" WHERE albumview.strAlbum <> ''");
sb.append(" AND album_artist.idArtist = " + artist.id + " GROUP BY albumview.idAlbum");
sb.append(albumsOrderBy(sortBy, sortOrder));
return parseAlbums(mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Gets all albums of with at least one song in a genre
* @param genre Genre
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return Albums of a genre
*/
public ArrayList<Album> getAlbums(INotifiableManager manager, Genre genre, int sortBy, String sortOrder) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT albumview.idAlbum, strAlbum, group_concat(DISTINCT strArtist) AS strArtists, iYear, art.url");
sb.append(" FROM albumview");
sb.append(" LEFT OUTER JOIN album_artist ON album_artist.idAlbum=albumview.idAlbum");
sb.append(" LEFT OUTER JOIN artist ON album_artist.idArtist=artist.idArtist");
sb.append(" LEFT OUTER JOIN art ON art.media_id=albumview.idAlbum AND art.media_type='album' AND art.type='thumb'");
sb.append(" WHERE albumview.strAlbum <> ''");
sb.append(" AND (albumview.idAlbum IN (");
sb.append(" SELECT song.idAlbum FROM song");
sb.append(" JOIN song_genre ON song.idSong = song_genre.idSong");
sb.append(" WHERE song_genre.idGenre = " + genre.id);
sb.append(" )) GROUP BY albumview.idAlbum");
sb.append(albumsOrderBy(sortBy, sortOrder));
return parseAlbums(mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Gets all albums from database
* @param albumArtistsOnly If set to true, hide artists who appear only on compilations.
* @return All albums
*/
public ArrayList<Artist> getArtists(INotifiableManager manager, boolean albumArtistsOnly) {
StringBuilder sb = new StringBuilder();
if (albumArtistsOnly) {
sb.append("SELECT idArtist, strArtist, art.url ");
sb.append(" FROM artist");
sb.append(" LEFT JOIN art ON art.media_id=idArtist AND art.media_type='artist' AND art.type='thumb'");
sb.append(" WHERE (");
sb.append(" idArtist IN (");
sb.append(" SELECT album.idArtist");
sb.append(" FROM album");
sb.append(" ) OR idArtist IN (");
sb.append(" SELECT exartistalbum.idArtist");
sb.append(" FROM exartistalbum");
sb.append(" JOIN album ON album.idAlbum = exartistalbum.idAlbum");
sb.append(" WHERE album.strExtraArtists != ''");
sb.append(" )");
sb.append(") AND artist.strArtist != ''");
} else {
sb.append("SELECT idArtist, strArtist, art.url FROM artist");
sb.append(" LEFT JOIN art ON art.media_id=idArtist AND art.media_type='artist' AND art.type='thumb'");
}
sb.append(" ORDER BY upper(strArtist), strArtist");
return parseArtists(mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Gets all artists with at least one song of a genre.
* @param genre Genre
* @param albumArtistsOnly If set to true, hide artists who appear only on compilations.
* @return Albums with a genre
*/
public ArrayList<Artist> getArtists(INotifiableManager manager, Genre genre, boolean albumArtistsOnly) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT DISTINCT idArtist, strArtist, art.url ");
sb.append(" FROM artist");
sb.append(" LEFT JOIN art ON art.media_id=idArtist AND art.media_type='artist' AND art.type='thumb'");
sb.append(" WHERE (idArtist IN (");
sb.append(" SELECT DISTINCT s.idArtist");
sb.append(" FROM song_genre AS g, song_artist AS s");
sb.append(" WHERE g.idGenre = " + genre.id);
sb.append(" AND g.idSong = s.idSong");
sb.append(" ))");
if (albumArtistsOnly) {
sb.append(" AND (");
sb.append(" idArtist IN (");
sb.append(" SELECT album.idArtist");
sb.append(" FROM album");
sb.append(" ) OR idArtist IN (");
sb.append(" SELECT exartistalbum.idArtist");
sb.append(" FROM exartistalbum");
sb.append(" JOIN album ON album.idAlbum = exartistalbum.idAlbum");
sb.append(" WHERE album.strExtraArtists != ''");
sb.append(" )");
sb.append(" ) AND artist.strArtist != ''");
}
sb.append(" ORDER BY upper(strArtist), strArtist");
return parseArtists(mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Gets all genres from database
* @return All genres
*/
public ArrayList<Genre> getGenres(INotifiableManager manager) {
return parseGenres(mConnection.query("QueryMusicDatabase", "SELECT idGenre, strGenre FROM genre ORDER BY upper(strGenre), strGenre", manager));
}
/**
* Updates the album object with additional data from the albuminfo table
* @param album
* @return Updated album
*/
public Album updateAlbumInfo(INotifiableManager manager, Album album) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT g.strGenre, ai.strLabel, ai.iRating");
sb.append(" FROM album_genre a");
sb.append(" LEFT JOIN genre g ON a.idGenre=g.idGenre");
sb.append(" LEFT JOIN albuminfo AS ai ON ai.idAlbum = a.idAlbum");
sb.append(" WHERE a.idGenre = g.idGenre");
sb.append(" AND a.idAlbum = " + album.id);
return parseAlbumInfo(album, mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Updates the artist object with additional data from the artistinfo table
* @param artist
* @return Updated artist
*/
public Artist updateArtistInfo(INotifiableManager manager, Artist artist) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT strBorn, strFormed, strGenres, strMoods, strStyles, strBiography");
sb.append(" FROM artistinfo");
sb.append(" WHERE idArtist = " + artist.id);
return parseArtistInfo(artist, mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Returns a list containing tracks of a certain condition.
* @param sqlCondition SQL condition which tracks to return
* @return Found tracks
*/
private ArrayList<Song> getSongs(INotifiableManager manager, StringBuilder sqlCondition, int sortBy, String sortOrder) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT songview.idSong, strTitle, group_concat(DISTINCT strArtist) AS strArtists, strAlbum, iTrack, iDuration, strPath, strFileName, art.url");
sb.append(" FROM songview");
sb.append(" LEFT OUTER JOIN song_artist ON song_artist.idSong=songview.idSong");
sb.append(" LEFT OUTER JOIN artist ON song_artist.idArtist=artist.idArtist");
sb.append(" LEFT OUTER JOIN art ON art.media_id=songview.idSong AND art.media_type='song' AND art.type='thumb'");
sb.append(" WHERE ");
sb.append(sqlCondition);
sb.append(" GROUP BY idSong");
sb.append(songsOrderBy(sortBy, sortOrder));
return parseSongs(mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Returns a hash map containing tracks of a certain condition.
* @param sqlCondition SQL condition which tracks to return
* @return Found tracks
*
private HashMap<Integer, Song> getSongsAsHashMap(StringBuilder sqlCondition) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT idSong, strTitle, strArtist, strAlbum, iTrack, iDuration, strPath, strFileName, strThumb");
sb.append(" FROM songview WHERE ");
sb.append(sqlCondition);
sb.append(" ORDER BY iTrack, strFileName");
return parseSongsAsHashMap(mConnection.query("QueryMusicDatabase", sb.toString()));
}*/
/**
* Returns the SQL condition that returns all songs of a song.
* @param song Song
* @return SQL string
*/
private StringBuilder getSongsCondition(Song song) {
final StringBuilder sb = new StringBuilder();
sb.append("idSong = ");
sb.append(song.id);
return sb;
}
/**
* Returns the SQL condition that returns all songs of an album.
* @param album Album
* @return SQL string
*/
private StringBuilder getSongsCondition(Album album) {
final StringBuilder sb = new StringBuilder();
sb.append("idAlbum = ");
sb.append(album.id);
return sb;
}
/**
* Returns the SQL condition that returns all songs of an artist.
* @param artist Artist
* @return SQL string
*/
private StringBuilder getSongsCondition(Artist artist) {
final StringBuilder sb = new StringBuilder();
//sb.append("(");
sb.append(" song_artist.idArtist = ");
sb.append(artist.id);
/*sb.append(" OR idSong IN (");
sb.append(" SELECT exartistsong.idSong");
sb.append(" FROM exartistsong");
sb.append(" WHERE exartistsong.idArtist = ");
sb.append(artist.id);
sb.append(" ) OR idSong IN (");
sb.append(" SELECT song.idSong");
sb.append(" FROM song");
sb.append(" JOIN album ON song.idAlbum = album.idAlbum");
sb.append(" WHERE album.idArtist = ");
sb.append(artist.id);
sb.append(" ) OR idSong IN (");
sb.append(" SELECT song.idSong");
sb.append(" FROM song");
sb.append(" JOIN exartistalbum ON song.idAlbum = exartistalbum.idAlbum");
sb.append(" JOIN album ON song.idAlbum = album.idAlbum");
sb.append(" WHERE exartistalbum.idArtist = ");
sb.append(artist.id);
sb.append(" AND album.strExtraArtists != ''");
sb.append(" )");
sb.append(")");*/
return sb;
}
/**
* Returns the SQL condition that returns all songs of a genre.
* @param genre Genre
* @return SQL string
*/
private StringBuilder getSongsCondition(Genre genre) {
final StringBuilder sb = new StringBuilder();
sb.append(" songview.idSong IN (");
sb.append(" SELECT song_genre.idSong FROM song_genre WHERE song_genre.idGenre = ");
sb.append(genre.id);
sb.append(")");
return sb;
}
/**
* Returns the SQL condition that returns all songs of a genre AND an artist.
* @param artist Artist
* @param genre Genre
* @return SQL string
*/
private StringBuilder getSongsCondition(Artist artist, Genre genre) {
final StringBuilder sb = new StringBuilder();
sb.append("(");
sb.append(" idArtist = ");
sb.append(artist.id);
sb.append(" OR idSong IN (");
sb.append(" SELECT exartistsong.idSong");
sb.append(" FROM exartistsong");
sb.append(" WHERE exartistsong.idArtist = ");
sb.append(artist.id);
sb.append(" ) OR idSong IN (");
sb.append(" SELECT song.idSong");
sb.append(" FROM song");
sb.append(" JOIN album ON song.idAlbum = album.idAlbum");
sb.append(" WHERE album.idArtist = ");
sb.append(artist.id);
sb.append(" ) OR idSong IN (");
sb.append(" SELECT song.idSong");
sb.append(" FROM song");
sb.append(" JOIN exartistalbum ON song.idAlbum = exartistalbum.idAlbum");
sb.append(" JOIN album ON song.idAlbum = album.idAlbum");
sb.append(" WHERE exartistalbum.idArtist = ");
sb.append(artist.id);
sb.append(" AND album.strExtraArtists != ''");
sb.append(" )");
sb.append(") AND (");
sb.append(" idGenre = ");
sb.append(genre.id);
sb.append(" OR idSong IN (");
sb.append(" SELECT exgenresong.idSong FROM exgenresong WHERE exgenresong.idGenre = ");
sb.append(genre.id);
sb.append(" )");
sb.append(")");
return sb;
}
/**
* Returns a list containing all tracks of an album. The list is sorted by filename.
* @param album Album
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return All tracks of an album
*/
public ArrayList<Song> getSongs(INotifiableManager manager, Album album, int sortBy, String sortOrder) {
return getSongs(manager, getSongsCondition(album), sortBy, sortOrder);
}
/**
* Returns a list containing all tracks of an artist. The list is sorted by album name, filename.
* @param artist Artist
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return All tracks of the artist
*/
public ArrayList<Song> getSongs(INotifiableManager manager, Artist artist, int sortBy, String sortOrder) {
return getSongs(manager, getSongsCondition(artist), sortBy, sortOrder);
}
/**
* Returns a list containing all tracks of a genre. The list is sorted by artist, album name, filename.
* @param genre Genre
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return All tracks of the genre
*/
public ArrayList<Song> getSongs(INotifiableManager manager, Genre genre, int sortBy, String sortOrder) {
return getSongs(manager, getSongsCondition(genre), sortBy, sortOrder);
}
/**
* Returns a list containing all tracks of a genre AND and artist. The list is sorted by
* artist, album name, filename.
* @param genre Genre
* @param sortBy Sort field, see SortType.*
* @param sortOrder Sort order, must be either SortType.ASC or SortType.DESC.
* @return All tracks of the genre
*/
public ArrayList<Song> getSongs(INotifiableManager manager, Artist artist, Genre genre, int sortBy, String sortOrder) {
return getSongs(manager, getSongsCondition(artist, genre), sortBy, sortOrder);
}
/**
* Returns a pre-resized album/artist cover. Pre-resizing is done in a way that
* the bitmap at least as large as the specified size but not larger than
* the double.
* @param manager Postback manager
* @param cover Cover object
* @param size Minimal size to pre-resize to.
* @return Thumbnail bitmap
*/
public Bitmap getCover(INotifiableManager manager, ICoverArt cover, int size) {
if (cover instanceof Artist) {
return getCover(manager, cover, size, Artist.getThumbUri(cover), Artist.getFallbackThumbUri(cover));
} else {
return getCover(manager, cover, size, Album.getThumbUri(cover), Album.getFallbackThumbUri(cover));
}
}
/**
* Returns a list containing all artist IDs that stand for "compilation".
* Best case scenario would be only one ID for "Various Artists", though
* there are also just "V.A." or "VA" naming conventions.
* @return List of compilation artist IDs
*/
public ArrayList<Integer> getCompilationArtistIDs(INotifiableManager manager) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT idArtist");
sb.append(" FROM artist");
sb.append(" WHERE lower(strArtist) LIKE 'various artists%%'");
sb.append(" OR lower(strArtist) LIKE 'v.a.%%'");
sb.append(" OR lower(strArtist) = 'va'");
return parseIntArray(mConnection.query("QueryMusicDatabase", sb.toString(), manager));
}
/**
* Returns an SQL String of given sort options of albums query
* @param sortBy Sort field
* @param sortOrder Sort order
* @return SQL "ORDER BY" string
*/
private String albumsOrderBy(int sortBy, String sortOrder) {
switch (sortBy) {
default:
case SortType.ALBUM:
return " ORDER BY lower(strAlbum) " + sortOrder;
case SortType.ARTIST:
return " ORDER BY lower(strArtist) " + sortOrder + ", lower(strAlbum) " + sortOrder;
case SortType.TRACK:
return " ORDER BY iTrack " + sortOrder + ", lower(strFileName) " + sortOrder;
case SortType.YEAR:
return " ORDER BY iYear " + sortOrder + ", lower(strAlbum) " + sortOrder;
}
}
/**
* Returns an SQL String of given sort options of songs query
* @param sortBy Sort field
* @param sortOrder Sort order
* @return SQL "ORDER BY" string
*/
private String songsOrderBy(int sortBy, String sortOrder) {
switch (sortBy) {
case SortType.ALBUM:
return " ORDER BY lower(strAlbum) " + sortOrder + ", iTrack " + sortOrder;
case SortType.ARTIST:
return " ORDER BY lower(strArtist) " + sortOrder + ", lower(strAlbum) " + sortOrder + ", iTrack " + sortOrder;
case SortType.TITLE:
return " ORDER BY lower(strTitle)" + sortOrder;
case SortType.FILENAME:
return " ORDER BY lower(strFileName)" + sortOrder;
default:
case SortType.TRACK:
return " ORDER BY iTrack " + sortOrder + ", lower(strFileName) " + sortOrder;
case SortType.DONT_SORT:
return "";
}
}
/**
* Converts query response from HTTP API to a list of Album objects. Each
* row must return the following attributes in the following order:
* <ol>
* <li><code>idAlbum</code></li>
* <li><code>strAlbum</code></li>
* <li><code>strArtist</code></li>
* <li><code>iYear</code></li>
* <li><code>strThumb</code></li>
* </ol>
* @param response
* @return List of albums
*/
private ArrayList<Album> parseAlbums(String response) {
ArrayList<Album> albums = new ArrayList<Album>();
String[] fields = response.split("<field>");
try {
for (int row = 1; row < fields.length; row += 5) {
albums.add(new Album(
Connection.trimInt(fields[row]),
Connection.trim(fields[row + 1]),
Connection.trim(fields[row + 2]),
Connection.trimInt(fields[row + 3]),
Connection.trim(fields[row + 4])
));
}
} catch (Exception e) {
System.err.println("ERROR: " + e.getMessage());
System.err.println("response = " + response);
e.printStackTrace();
}
return albums;
}
/**
* Updates an album with info from HTTP API query response. One row is
* expected, with the following columns:
* <ol>
* <li><code>strGenre</code></li>
* <li><code>strExtraGenres</code></li>
* <li><code>strLabel</code></li>
* <li><code>iRating</code></li>
* </ol>
* @param album
* @param response
* @return Updated album
*/
private Album parseAlbumInfo(Album album, String response) {
String[] fields = response.split("<field>");
try {
if (Connection.trim(fields[1]).length() > 0) {
album.genres = Connection.trim(fields[1]);
}
if (Connection.trim(fields[2]).length() > 0) {
album.label = Connection.trim(fields[2]);
}
if (Connection.trim(fields[3]).length() > 0) {
album.rating = Connection.trimInt(fields[3]);
}
} catch (Exception e) {
System.err.println("ERROR: " + e.getMessage());
System.err.println("response = " + response);
e.printStackTrace();
}
return album;
}
/**
* Updates an artist with info from HTTP API query response. One row is
* expected, with the following columns:
* <ol>
* <li><code>strBorn</code></li>
* <li><code>strFormed</code></li>
* <li><code>strGenre</code></li>
* <li><code>strMoods</code></li>
* <li><code>strStyles</code></li>
* <li><code>strBiography</code></li>
* </ol>
* @param artist
* @param response
* @return Updated artist
*/
private Artist parseArtistInfo(Artist artist, String response) {
String[] fields = response.split("<field>");
try {
if (Connection.trim(fields[1]).length() > 0) {
artist.born = Connection.trim(fields[1]);
}
if (Connection.trim(fields[2]).length() > 0) {
artist.formed = Connection.trim(fields[2]);
}
if (Connection.trim(fields[3]).length() > 0) {
artist.genres = Connection.trim(fields[3]);
}
if (Connection.trim(fields[4]).length() > 0) {
artist.moods = Connection.trim(fields[4]);
}
if (Connection.trim(fields[5]).length() > 0) {
artist.styles = Connection.trim(fields[5]);
}
if (Connection.trim(fields[6]).length() > 0) {
artist.biography = Connection.trim(fields[6]);
}
} catch (Exception e) {
System.err.println("ERROR: " + e.getMessage());
System.err.println("response = " + response);
e.printStackTrace();
}
return artist;
}
/**
* Converts query response from HTTP API to a list of Song objects. Each
* row must return the following columns in the following order:
* <ol>
* <li><code>idSong</code></li>
* <li><code>strTitle</code></li>
* <li><code>strArtist</code></li>
* <li><code>strAlbum</code></li>
* <li><code>iTrack</code></li>
* <li><code>iDuration</code></li>
* <li><code>strPath</code></li>
* <li><code>strFileName</code></li>
* <li><code>strThumb</code></li>
* </ol>
* @param response
* @return List of Songs
*/
private ArrayList<Song> parseSongs(String response) {
ArrayList<Song> songs = new ArrayList<Song>();
String[] fields = response.split("<field>");
try {
for (int row = 1; row < fields.length; row += 9) {
songs.add(new Song( // int id, String title, String artist, String album, int track, int duration, String path, String filename, String thumbPath
Connection.trimInt(fields[row]),
Connection.trim(fields[row + 1]),
Connection.trim(fields[row + 2]),
Connection.trim(fields[row + 3]),
Connection.trimInt(fields[row + 4]),
Connection.trimInt(fields[row + 5]),
Connection.trim(fields[row + 6]),
Connection.trim(fields[row + 7]),
Connection.trim(fields[row + 8])
));
}
} catch (Exception e) {
System.err.println("ERROR: " + e.getMessage());
System.err.println("response = " + response);
e.printStackTrace();
}
return songs;
}
/**
* Converts query response from HTTP API to a list of Song objects. Each
* row must return the following columns in the following order:
* <ol>
* <li><code>idSong</code></li>
* <li><code>strTitle</code></li>
* <li><code>strArtist</code></li>
* <li><code>strAlbum</code></li>
* <li><code>iTrack</code></li>
* <li><code>iDuration</code></li>
* <li><code>strPath</code></li>
* <li><code>strFileName</code></li>
* <li><code>strThumb</code></li>
* </ol>
* @param response
* @return List of Songs
*
private HashMap<Integer, Song> parseSongsAsHashMap(String response) {
HashMap<Integer, Song> songs = new HashMap<Integer, Song>();
String[] fields = response.split("<field>");
try {
for (int row = 1; row < fields.length; row += 9) {
songs.put(Connection.trimInt(fields[row]),
new Song( // int id, String title, String artist, String album, int track, int duration, String path, String filename, String thumbPath
Connection.trimInt(fields[row]),
Connection.trim(fields[row + 1]),
Connection.trim(fields[row + 2]),
Connection.trim(fields[row + 3]),
Connection.trimInt(fields[row + 4]),
Connection.trimInt(fields[row + 5]),
Connection.trim(fields[row + 6]),
Connection.trim(fields[row + 7]),
Connection.trim(fields[row + 8])
)
);
}
} catch (Exception e) {
System.err.println("ERROR: " + e.getMessage());
System.err.println("response = " + response);
e.printStackTrace();
}
return songs;
}*/
/**
* Converts query response from HTTP API to a list of integer values.
* @param response
* @return
*/
private ArrayList<Integer> parseIntArray(String response) {
ArrayList<Integer> array = new ArrayList<Integer>();
String[] fields = response.split("<field>");
try {
for (int row = 1; row < fields.length; row += 9) {
array.add(Connection.trimInt(fields[row]));
}
} catch (Exception e) {
System.err.println("ERROR: " + e.getMessage());
System.err.println("response = " + response);
e.printStackTrace();
}
return array;
}
/**
* Converts query response from HTTP API to a list of Artist objects. Each
* row must return the following columns in the following order:
* <ol>
* <li><code>idArtist</code></li>
* <li><code>strArtist</code></li>
* </ol>
* @param response
* @return List of Artists
*/
private ArrayList<Artist> parseArtists(String response) {
ArrayList<Artist> artists = new ArrayList<Artist>();
String[] fields = response.split("<field>");
try {
for (int row = 1; row < fields.length; row += 3) {
artists.add(new Artist(
Connection.trimInt(fields[row]),
Connection.trim(fields[row + 1]),
Connection.trim(fields[row + 2])
));
}
} catch (Exception e) {
System.err.println("ERROR: " + e.getMessage());
System.err.println("response = " + response);
e.printStackTrace();
}
return artists;
}
/**
* Converts query response from HTTP API to a list of Genre objects. Each
* row must return the following columns in the following order:
* <ol>
* <li><code>idGenre</code></li>
* <li><code>strGenre</code></li>
* </ol>
* @param response
* @return List of Genres
*/
private ArrayList<Genre> parseGenres(String response) {
ArrayList<Genre> genres = new ArrayList<Genre>();
String[] fields = response.split("<field>");
try {
for (int row = 1; row < fields.length; row += 2) {
genres.add(new Genre(
Connection.trimInt(fields[row]),
Connection.trim(fields[row + 1])
));
}
} catch (Exception e) {
System.err.println("ERROR: " + e.getMessage());
System.err.println("response = " + response);
e.printStackTrace();
}
return genres;
}
static ICurrentlyPlaying getCurrentlyPlaying(final HashMap<String, String> map) {
return new IControlClient.ICurrentlyPlaying() {
private static final long serialVersionUID = 5036994329211476714L;
public String getTitle() {
return map.get("Title");
}
public int getTime() {
return parseTime(map.get("Time"));
}
public int getPlayStatus() {
return PlayStatus.parse(map.get("PlayStatus"));
}
public int getPlaylistPosition() {
return Integer.parseInt(map.get("SongNo"));
}
//Workarond for bug in Float.valueOf(): http://code.google.com/p/android/issues/detail?id=3156
public float getPercentage() {
try{
return Integer.valueOf(map.get("Percentage"));
} catch (NumberFormatException e) { }
return Float.valueOf(map.get("Percentage"));
}
public String getFilename() {
return map.get("Filename");
}
public int getDuration() {
return parseTime(map.get("Duration"));
}
public String getArtist() {
return map.get("Artist");
}
public String getAlbum() {
return map.get("Album");
}
public int getMediaType() {
return MediaType.MUSIC;
}
public boolean isPlaying() {
return PlayStatus.parse(map.get("PlayStatus")) == PlayStatus.PLAYING;
}
public int getHeight() {
return 0;
}
public int getWidth() {
return 0;
}
private int parseTime(String time) {
String[] s = time.split(":");
if (s.length == 2) {
return Integer.parseInt(s[0]) * 60 + Integer.parseInt(s[1]);
} else if (s.length == 3) {
return Integer.parseInt(s[0]) * 3600 + Integer.parseInt(s[1]) * 60 + Integer.parseInt(s[2]);
} else {
return 0;
}
}
};
}
}