aboutsummaryrefslogblamecommitdiffstats
path: root/src/mpdclient.c
blob: f5939e475e03e90cd020684978842b6a1865d1fc (plain) (tree)
1
2
3
4
5
6
7
8
9



                                                



                                                                       
 



                                                                  




                                                                          
 

                         
                   
                    
                     
 




                   


                                                                           
                      
                        
 

                    

                                                                             

                                                   
 


                                


                                                        
                                      

                  

                                                                 


                                                         


                                                             
 
                 

 

                         


                                                           
                                      


                                          

                                                                 



                                                    

                                                                            

                                      
 
                 

 
 



                                                
                                         
 

                                                                         
 





                                                
 

                                      
 
 





                                                                              
                                        
 


                                         
                                                  
 

                                                                           

                                        
 


                                                                        
                                                            
                             
         
 
                 




                   
                       
 
                                           
                                    
 
                 

 
    

                              
                                


                                              



                                           




                                    


                                                   
 


                                          
 
                                     
 



                               


    


                                 
                                  

                                  
                        
 


                                        
 
                            
                                                                


                                                         
 







                                                                 




                                
                        
 

                          
 








                                                 
 

                                                    
                                                    



                                                                      
 



                                                                
 
                               
 
                      






                                                                              
    
                                            

                     
                                                          
 



                                                                            
     
                                                
      

                                           

 
    

                                               

                                                   

 
    










                                              















                                                                             





                                           

                                  

                                           

 
    

                                  


                                           

 
    

                                  


                                           

 
    

                                                     

                                                      

 
    

                                     


                                              

 
    

                                   
                        
 





                                                                   

 
    

                                                

                                                    

 
    

                                                

                                                    

 
    

                                                   

                                                       

 
    
                                                    
 

                                                               

 
    

                                                

                                                    

 
    
                                                        
 

                                                     

 
    
                                                        

                        
 

                                  
 



                                                      

                                              
                                      
                                            
 

                                                                           
 

                                                                            
     
                              

      
                 


    
                                              
 
                        
                              
 
                                                                   
                          
 

                                               
                                            
                     
                                                         
     
                                                  
      

                                                  

                                                 

                                                                          
 
                                               
                                                 
 

                                                                               
 




                                           
 

                           
     
                              

      
                 




                                                                  
               
                                       
 
                                                      
                                                      
                          
 

                                                      
 
                                          
                     
                                                                   
     
                                                                 
      

                                             

                                               

                                                          
 

                                                                           
 
     

                              
 
                                            
                                                                                   
 
                 

 
    
                                                                 
 
                        
 



                                                                          

 
    
                                                                 
 


                                                          

 
    
                                                                   
 
                        
 



                                                                            





                                                                              
 


                                                                         

                                               
 


                                   




                                                                     
                                                                 




                                                                     
                                                                         




                                                                     
                                                                         




                                                                   
                                                               





                                                                   
                                                                     




                                                                   
                                                                     




                                                                    
                                                                   




                                                                   
                                                                   

 
 



                                                                              
                     
    

                                         
                               
 

                          
 
                                     
 

                                                                 


                                                                         
                                           
         
 

                                             
 



                                                                     

 
                       
 
                                 
    

                                                 
                               
 

                          
 
                                                                
 
                                                                         
                                                          
 
                                                                                 
                                         
                                                                        

                                            
                                                            
                 

                                           
         
 
                                   

                                                                          
 
                                                    
                                                   
         
 

                                             
 
                                                                     
 
                 
 
 
     
    

                                                 
                                            

      
 




                                                                              
                      
                                                         
 

                                       

                                      
                                                   

                                                 
                                                
                                                
 
                                                               
                                                  
 


                                                                                      
         
 


                                                                              
                                                                           
                          
                                                                   

                        


                      



                                             
 

                                       
 



                                                                         
                                      
 

                                                             
 
                                          
                                        

                            
 
                        


                      

                                                                         

                                                       
 





                                                           

 


                                                                    
                
 
                                  
                         
 
                                                


                                                              
                                                        
 

                                                                          
 

                                                                      
         
 


                                              
 
       
                                     
 

                           
 


                                                                   
 



                                              
 
 
       
                                                        
 

                           
 


                                                                         
 

                                              
 

                    
/* ncmpc (Ncurses MPD Client)
 * (c) 2004-2009 The Music Player Daemon Project
 * Project homepage: http://musicpd.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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "mpdclient.h"
#include "screen_utils.h"
#include "config.h"
#include "options.h"
#include "strfsong.h"

#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>

#undef  ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
#define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
#define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
#define ENABLE_SONG_ID
#define ENABLE_PLCHANGES

#define BUFSIZE 1024

#define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)

/* from utils.c */
extern GList *string_list_free(GList *string_list);


/* filelist sorting functions */
static gint
compare_filelistentry_dir(gconstpointer filelist_entry1,
			  gconstpointer filelist_entry2)
{
	const mpd_InfoEntity *e1, *e2;
	int n = 0;

	e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
	e2 = ((const filelist_entry_t *)filelist_entry2)->entity;

	if (e1 && e2 &&
	    e1->type == MPD_INFO_ENTITY_TYPE_DIRECTORY &&
	    e2->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
		n = g_utf8_collate(e1->info.directory->path,
				   e2->info.directory->path);

	return n;
}

/* sort by list-format */
gint
compare_filelistentry_format(gconstpointer filelist_entry1,
			     gconstpointer filelist_entry2)
{
	const mpd_InfoEntity *e1, *e2;
	char key1[BUFSIZE], key2[BUFSIZE];
	int n = 0;

	e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
	e2 = ((const filelist_entry_t *)filelist_entry2)->entity;

	if (e1 && e2 &&
	    e1->type == MPD_INFO_ENTITY_TYPE_SONG &&
	    e2->type == MPD_INFO_ENTITY_TYPE_SONG) {
		strfsong(key1, BUFSIZE, options.list_format, e1->info.song);
		strfsong(key2, BUFSIZE, options.list_format, e2->info.song);
		n = strcmp(key1,key2);
	}

	return n;
}


/* Error callbacks */
static gint
error_cb(mpdclient_t *c, gint error, gchar *msg)
{
	GList *list = c->error_callbacks;

	if (list == NULL)
		fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);

	while (list) {
		mpdc_error_cb_t cb = list->data;
		if (cb)
			cb(c, error, msg);
		list = list->next;
	}

	mpd_clearError(c->connection);
	return error;
}


/****************************************************************************/
/*** mpdclient functions ****************************************************/
/****************************************************************************/

gint
mpdclient_finish_command(mpdclient_t *c)
{
	mpd_finishCommand(c->connection);

	if (c->connection->error) {
		gint error = c->connection->error;

		if (error == MPD_ERROR_ACK &&
		    c->connection->errorCode == MPD_ACK_ERROR_PERMISSION &&
		    screen_auth(c) == 0)
			return 0;

		if (error == MPD_ERROR_ACK)
			error = error | (c->connection->errorCode << 8);

		error_cb(c, error, c->connection->errorStr);
		return error;
	}

	return 0;
}

mpdclient_t *
mpdclient_new(void)
{
	mpdclient_t *c;

	c = g_malloc0(sizeof(mpdclient_t));
	playlist_init(&c->playlist);

	return c;
}

void
mpdclient_free(mpdclient_t *c)
{
	mpdclient_disconnect(c);

	mpdclient_playlist_free(&c->playlist);

	g_list_free(c->error_callbacks);
	g_list_free(c->playlist_callbacks);
	g_list_free(c->browse_callbacks);
	g_free(c);
}

gint
mpdclient_disconnect(mpdclient_t *c)
{
	if (c->connection)
		mpd_closeConnection(c->connection);
	c->connection = NULL;

	if (c->status)
		mpd_freeStatus(c->status);
	c->status = NULL;

	playlist_clear(&c->playlist);

	if (c->song)
		c->song = NULL;

	return 0;
}

gint
mpdclient_connect(mpdclient_t *c,
		  gchar *host,
		  gint port,
		  gfloat _timeout,
		  gchar *password)
{
	gint retval = 0;

	/* close any open connection */
	if( c->connection )
		mpdclient_disconnect(c);

	/* connect to MPD */
	c->connection = mpd_newConnection(host, port, _timeout);
	if( c->connection->error )
		return error_cb(c, c->connection->error,
				c->connection->errorStr);

	/* send password */
	if( password ) {
		mpd_sendPasswordCommand(c->connection, password);
		retval = mpdclient_finish_command(c);
	}
	c->need_update = TRUE;

	return retval;
}

gint
mpdclient_update(mpdclient_t *c)
{
	gint retval = 0;

	if (MPD_ERROR(c))
		return -1;

	/* free the old status */
	if (c->status)
		mpd_freeStatus(c->status);

	/* retreive new status */
	mpd_sendStatusCommand(c->connection);
	c->status = mpd_getStatus(c->connection);
	if ((retval=mpdclient_finish_command(c)))
		return retval;

	/* check if the playlist needs an update */
	if (c->playlist.id != c->status->playlist) {
		if (playlist_is_empty(&c->playlist))
			retval = mpdclient_playlist_update_changes(c);
		else
			retval = mpdclient_playlist_update(c);
	}

	/* update the current song */
	if (!c->song || c->status->songid != c->song->id) {
		c->song = playlist_get_song(c, c->status->song);
	}

	c->need_update = FALSE;

	return retval;
}


/****************************************************************************/
/*** MPD Commands  **********************************************************/
/****************************************************************************/

gint
mpdclient_cmd_play(mpdclient_t *c, gint idx)
{
#ifdef ENABLE_SONG_ID
	struct mpd_song *song = playlist_get_song(c, idx);

	if (song)
		mpd_sendPlayIdCommand(c->connection, song->id);
	else
		mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
#else
	mpd_sendPlayCommand(c->connection, idx);
#endif
	c->need_update = TRUE;
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_pause(mpdclient_t *c, gint value)
{
	mpd_sendPauseCommand(c->connection, value);
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_crop(mpdclient_t *c)
{
	mpd_Status *status;
	int length;

	mpd_sendStatusCommand(c->connection);
	status = mpd_getStatus(c->connection);
	length = status->playlistLength - 1;

	if (length <= 0) {
		mpd_freeStatus(status);
	} else if (status->state == 3 || status->state == 2) {
		/* If playing or paused */

		mpd_sendCommandListBegin( c->connection );

		while (length >= 0) {
			if (length != status->song)
				mpd_sendDeleteCommand(c->connection, length);

			length--;
		}

		mpd_sendCommandListEnd(c->connection);
		mpd_freeStatus(status);
	} else {
		mpd_freeStatus(status);
	}

	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_stop(mpdclient_t *c)
{
	mpd_sendStopCommand(c->connection);
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_next(mpdclient_t *c)
{
	mpd_sendNextCommand(c->connection);
	c->need_update = TRUE;
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_prev(mpdclient_t *c)
{
	mpd_sendPrevCommand(c->connection);
	c->need_update = TRUE;
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
{
	mpd_sendSeekIdCommand(c->connection, id, pos);
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_shuffle(mpdclient_t *c)
{
	mpd_sendShuffleCommand(c->connection);
	c->need_update = TRUE;
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_clear(mpdclient_t *c)
{
	gint retval = 0;

	mpd_sendClearCommand(c->connection);
	retval = mpdclient_finish_command(c);
	/* call playlist updated callback */
	mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
	c->need_update = TRUE;
	return retval;
}

gint
mpdclient_cmd_repeat(mpdclient_t *c, gint value)
{
	mpd_sendRepeatCommand(c->connection, value);
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_random(mpdclient_t *c, gint value)
{
	mpd_sendRandomCommand(c->connection, value);
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
{
	mpd_sendCrossfadeCommand(c->connection, value);
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_db_update(mpdclient_t *c, gchar *path)
{
	mpd_sendUpdateCommand(c->connection, path ? path : "");
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_volume(mpdclient_t *c, gint value)
{
	mpd_sendSetvolCommand(c->connection, value);
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_add_path(mpdclient_t *c, gchar *path_utf8)
{
	mpd_sendAddCommand(c->connection, path_utf8);
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_add(mpdclient_t *c, struct mpd_song *song)
{
	gint retval = 0;

	if( !song || !song->file )
		return -1;

	/* send the add command to mpd */
	mpd_sendAddCommand(c->connection, song->file);
	if( (retval=mpdclient_finish_command(c)) )
		return retval;

#ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
	/* add the song to playlist */
	playlist_append(&c->playlist, song);

	/* increment the playlist id, so we dont retrives a new playlist */
	c->playlist.id++;

	/* call playlist updated callback */
	mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
#else
	c->need_update = TRUE;
#endif

	return 0;
}

gint
mpdclient_cmd_delete(mpdclient_t *c, gint idx)
{
	gint retval = 0;
	struct mpd_song *song;

	if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
		return -1;

	song = playlist_get(&c->playlist, idx);

	/* send the delete command to mpd */
#ifdef ENABLE_SONG_ID
	mpd_sendDeleteIdCommand(c->connection, song->id);
#else
	mpd_sendDeleteCommand(c->connection, idx);
#endif
	if( (retval=mpdclient_finish_command(c)) )
		return retval;

#ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
	/* increment the playlist id, so we dont retrive a new playlist */
	c->playlist.id++;

	/* remove the song from the playlist */
	playlist_remove_reuse(&c->playlist, idx);

	/* call playlist updated callback */
	mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);

	/* remove references to the song */
	if (c->song == song) {
		c->song = NULL;
		c->need_update = TRUE;
	}

	mpd_freeSong(song);

#else
	c->need_update = TRUE;
#endif

	return 0;
}

gint
mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
{
	gint n;
	struct mpd_song *song1, *song2;

	if (old_index == new_index || new_index < 0 ||
	    (guint)new_index >= c->playlist.list->len)
		return -1;

	song1 = playlist_get(&c->playlist, old_index);
	song2 = playlist_get(&c->playlist, new_index);

	/* send the move command to mpd */
#ifdef ENABLE_SONG_ID
	mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
#else
	mpd_sendMoveCommand(c->connection, old_index, new_index);
#endif
	if( (n=mpdclient_finish_command(c)) )
		return n;

#ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
	/* update the playlist */
	playlist_swap(&c->playlist, old_index, new_index);

	/* increment the playlist id, so we dont retrives a new playlist */
	c->playlist.id++;

#else
	c->need_update = TRUE;
#endif

	/* call playlist updated callback */
	mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);

	return 0;
}

gint
mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename_utf8)
{
	gint retval = 0;

	mpd_sendSaveCommand(c->connection, filename_utf8);
	if ((retval = mpdclient_finish_command(c)) == 0)
		mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
	return retval;
}

gint
mpdclient_cmd_load_playlist(mpdclient_t *c, gchar *filename_utf8)
{
	mpd_sendLoadCommand(c->connection, filename_utf8);
	c->need_update = TRUE;
	return mpdclient_finish_command(c);
}

gint
mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename_utf8)
{
	gint retval = 0;

	mpd_sendRmCommand(c->connection, filename_utf8);
	if ((retval = mpdclient_finish_command(c)) == 0)
		mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
	return retval;
}


/****************************************************************************/
/*** Callback managment functions *******************************************/
/****************************************************************************/

static void
do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
{
	while (list) {
		mpdc_list_cb_t fn = list->data;

		fn(c, event, data);
		list = list->next;
	}
}

void
mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
{
	do_list_callbacks(c, c->playlist_callbacks, event, data);
}

void
mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
{
	c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
}

void
mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
{
	c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
}

void
mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
{
	do_list_callbacks(c, c->browse_callbacks, event, data);
}


void
mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
{
	c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
}

void
mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
{
	c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
}

void
mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
{
	c->error_callbacks = g_list_append(c->error_callbacks, cb);
}

void
mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
{
	c->error_callbacks = g_list_remove(c->error_callbacks, cb);
}


/****************************************************************************/
/*** Playlist managment functions *******************************************/
/****************************************************************************/

/* update playlist */
gint
mpdclient_playlist_update(mpdclient_t *c)
{
	mpd_InfoEntity *entity;

	if (MPD_ERROR(c))
		return -1;

	playlist_clear(&c->playlist);

	mpd_sendPlaylistInfoCommand(c->connection,-1);
	while ((entity = mpd_getNextInfoEntity(c->connection))) {
		if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
			playlist_append(&c->playlist, entity->info.song);

		mpd_freeInfoEntity(entity);
	}

	c->playlist.id = c->status->playlist;
	c->song = NULL;

	/* call playlist updated callbacks */
	mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);

	return mpdclient_finish_command(c);
}

#ifdef ENABLE_PLCHANGES

/* update playlist (plchanges) */
gint
mpdclient_playlist_update_changes(mpdclient_t *c)
{
	mpd_InfoEntity *entity;

	if (MPD_ERROR(c))
		return -1;

	mpd_sendPlChangesCommand(c->connection, c->playlist.id);

	while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) {
		struct mpd_song *song = entity->info.song;

		if (song->pos >= 0 && (guint)song->pos < c->playlist.list->len) {
			/* update song */
			playlist_replace(&c->playlist, song->pos, song);
		} else {
			/* add a new song */
			playlist_append(&c->playlist, song);
		}

		mpd_freeInfoEntity(entity);
	}

	/* remove trailing songs */
	while ((guint)c->status->playlistLength < c->playlist.list->len) {
		guint pos = c->playlist.list->len - 1;

		/* Remove the last playlist entry */
		playlist_remove(&c->playlist, pos);
	}

	c->song = NULL;
	c->playlist.id = c->status->playlist;

	mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);

	return 0;
}

#else
gint
mpdclient_playlist_update_changes(mpdclient_t *c)
{
	return mpdclient_playlist_update(c);
}
#endif


/****************************************************************************/
/*** Filelist functions *****************************************************/
/****************************************************************************/

mpdclient_filelist_t *
mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
{
	mpdclient_filelist_t *filelist;
	mpd_InfoEntity *entity;
	gboolean has_dirs_only = TRUE;

	mpd_sendLsInfoCommand(c->connection, path);
	filelist = filelist_new(path);
	if (path && path[0] && strcmp(path, "/"))
		/* add a dummy entry for ./.. */
		filelist_append(filelist, NULL);

	while ((entity=mpd_getNextInfoEntity(c->connection))) {
		filelist_append(filelist, entity);

		if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY) {
			has_dirs_only = FALSE;
		}
	}

	/* If there's an error, ignore it.  We'll return an empty filelist. */
	mpdclient_finish_command(c);

	// If there are only directory entities in the filelist, we sort it
	if (has_dirs_only)
		filelist_sort(filelist, compare_filelistentry_dir);

	return filelist;
}

mpdclient_filelist_t *
mpdclient_filelist_search(mpdclient_t *c,
			  int exact_match,
			  int table,
			  gchar *filter_utf8)
{
	mpdclient_filelist_t *filelist;
	mpd_InfoEntity *entity;

	if (exact_match)
		mpd_sendFindCommand(c->connection, table, filter_utf8);
	else
		mpd_sendSearchCommand(c->connection, table, filter_utf8);
	filelist = filelist_new(NULL);

	while ((entity=mpd_getNextInfoEntity(c->connection)))
		filelist_append(filelist, entity);

	if (mpdclient_finish_command(c)) {
		filelist_free(filelist);
		return NULL;
	}

	return filelist;
}

mpdclient_filelist_t *
mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
{
	if (filelist != NULL) {
		gchar *path = g_strdup(filelist->path);

		filelist_free(filelist);
		filelist = mpdclient_filelist_get(c, path);
		g_free(path);
		return filelist;
	}
	return NULL;
}

int
mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
{
	guint i;

	if (filelist_is_empty(fl))
		return 0;

	mpd_sendCommandListBegin(c->connection);

	for (i = 0; i < filelist_length(fl); ++i) {
		filelist_entry_t *entry = filelist_get(fl, i);
		mpd_InfoEntity *entity  = entry->entity;

		if (entity && entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
			struct mpd_song *song = entity->info.song;

			mpd_sendAddCommand(c->connection, song->file);
		}
	}

	mpd_sendCommandListEnd(c->connection);
	return mpdclient_finish_command(c);
}

GList *
mpdclient_get_artists(mpdclient_t *c)
{
	gchar *str = NULL;
	GList *list = NULL;

	mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
	while ((str = mpd_getNextArtist(c->connection)))
		list = g_list_append(list, (gpointer) str);

	if (mpdclient_finish_command(c))
		return string_list_free(list);

	return list;
}

GList *
mpdclient_get_albums(mpdclient_t *c, gchar *artist_utf8)
{
	gchar *str = NULL;
	GList *list = NULL;

	mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
	while ((str = mpd_getNextAlbum(c->connection)))
		list = g_list_append(list, (gpointer) str);

	if (mpdclient_finish_command(c))
		return string_list_free(list);

	return list;
}