aboutsummaryrefslogblamecommitdiffstats
path: root/src/mpdclient.c
blob: 57b470681a82832af39143a22c8c8060988202bd (plain) (tree)



















                                                                            

                         


                    
                    
                     
 




                   


                                                                           
                      
                         
 

                    

                                                                             

                                                   
 


                                


                                                        
                                      


                          

                                                                 









                                                                       
 
                 

 

                         


                                                           
                                      


                                          

                                                                 







                                                                    
 
                 

 
 






                                                
                                                             











                                      
              

                        









                                                           
  













                                                                              
                                        

                                                        



                                                                 
                              
                  
                   





























                                     

















                                          


                                 
                                  

                                  
                        
 


                                        
 
                            
                                                                


                                                         
 







                                                                 


















                                            
              




























                                                                              
    
                                            

                     
                                                   
 




                                                                            
     
                                                
      

                                           

 
    

                                               

                                                   

 
    

                                  

                                           

 
    

                                  


                                           

 
    

                                  


                                           




                                                     
                        














































                                                             
                                                         
 
                                                         










                                                






                                                             




                                                   
                                                   



                    
    
                                                 

                        
 

                                  
 



                                                      

                                              


                                                                              
 

                                                                           
 

                                                                            
     
                              

      
                 


    
                                              
 
                        
                                                   
 

                          
 
                                            
                     

                                                         
     
                                                  
      

                                                  

                                                 

                                                                          
 


                                                                            
 

                                                                               
 




                                           
 

                           

     
                              

      
                 




                                                                  



                                
 
                                                      
                                                   
                          
 

                                                
 
                                          
                     

                                                                   
     

                                                                 
      

                                             

                                               























                                                                                
 

                                                                           
 
     

                              
 


                                                                                   
 
                 


     
                                                                      

                  



                                                              









                                                            




                        
                                                                      






                                                    
                                                                        








                                                                










                                                                
















































































                                                                              

                                                          


                     
    

                                         
                               
 
                                                                       
 

                          
 

                                                      
 



                                                                        
 




                                                                          
         
 


                                             
 



                                                                     

 
                       
 
                                 
    

                                                 
                               
 

                                                                 
 

                          
 
                                                                
 

                                                                         
 
                                                                              
                                                                              
 










                                                                                      
         
 
                                   
                                                                       
                                                            
 





                                                                               
 



                                                             
 
                                                                     
 
                 
 
 
     
    

                                                 
                                            

      

          
                                           
 
                                                                   


       
                                       
 
                                                     
 







                                                         




                                             
                                       
 







                                                         

 
    

                                                  
                                                    

 
    

                                                   
                                                                           




























                                                                              
                                   

                    




















                                           
                                                         



                                          
                                
 
                                          

















                                                                       




                                                                          

     

                                                                         




                                  






                                                                              



                      



                                                  


                                 
 
                                                    



                                                             











                                                                       

                                             




                           

                      
                                         

                                          
                                         
 
                                       
                                                     
 
                                                      


                                                                        
 
                        

 
                      

                                                                         
                        


































                                                                      































                                                                    
 















                                                             
 

              
 




                                                             
 












                                                                   




 
/* 
 * $Id$
 *
 * (c) 2004 by Kalle Wallin <kaw@linux.se>
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "mpdclient.h"
#include "screen_utils.h"
#include "config.h"
#include "ncmpc.h"
#include "support.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;
	char *key1, *key2;
	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) {
		key1 = g_utf8_collate_key(e1->info.directory->path,-1);
		key2 = g_utf8_collate_key(e2->info.directory->path,-1);
		n = strcmp(key1,key2);
		g_free(key1);
		g_free(key2);
	}

	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, LIST_FORMAT, e1->info.song);
		strfsong(key2, BUFSIZE, 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;
}

#ifndef NDEBUG
// Unused ath the moment
/*
#include "strfsong.h"

static gchar *
get_song_name(mpd_Song *song)
{
  static gchar name[256];

  strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
  return name;
}
*/
#endif

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

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

  if( c->connection->error )
    {
      gchar *msg = locale_to_utf8(c->connection->errorStr);
      gint error = c->connection->error;
      if( error == MPD_ERROR_ACK )
	error = error | (c->connection->errorCode << 8);
      if(  c->connection->errorCode == MPD_ACK_ERROR_PERMISSION )
	{
	  if(screen_auth(c) == 0) return 0;
	}
      error_cb(c, error, msg);
      g_free(msg);
      return error;
    }

  return 0;
}

mpdclient_t *
mpdclient_new(void)
{
  mpdclient_t *c;

  c = g_malloc0(sizeof(mpdclient_t));

  return c;
}

mpdclient_t *
mpdclient_free(mpdclient_t *c)
{
  mpdclient_disconnect(c);
  g_list_free(c->error_callbacks);
  g_list_free(c->playlist_callbacks);
  g_list_free(c->browse_callbacks);
  g_free(c);

  return NULL;
}

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;

  if( c->playlist.list )
    mpdclient_playlist_free(&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;
#ifndef NDEBUG
  if( c->status->error )
    D("status> %s\n", c->status->error);
#endif

  /* check if the playlist needs an update */
  if( c->playlist.id != c->status->playlist )
    {
      if( c->playlist.list )
	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
	mpd_Song *song = playlist_get_song(c, idx);

	D("Play id:%d\n", song ? song->id : -1);
	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_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)
{
  D("Seek id:%d\n", id);
  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_utf8(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_utf8(mpdclient_t *c, gchar *path_utf8)
{
  mpd_sendAddCommand(c->connection, path_utf8);
  return mpdclient_finish_command(c);
}

gint 
mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
{
  gint retval;
  gchar *path_utf8 = locale_to_utf8(path);

  retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
  g_free(path_utf8);
  return retval;
}

gint
mpdclient_cmd_add(mpdclient_t *c, 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 */
	c->playlist.list = g_list_append(c->playlist.list, mpd_songDup(song));
	c->playlist.length++;

	/* 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;
	mpd_Song *song = playlist_get_song(c, idx);

	if( !song )
		return -1;

	/* send the delete command to mpd */
#ifdef ENABLE_SONG_ID
	D("Delete id:%d\n", 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 */
	c->playlist.list = g_list_remove(c->playlist.list, (gpointer) song);
	c->playlist.length = g_list_length(c->playlist.list);

	/* 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;
	}

	/* free song */
	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, index1, index2;
	GList *item1, *item2;
	gpointer data1, data2;
	mpd_Song *song1, *song2;

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

	song1 = playlist_get_song(c, old_index);
	song2 = playlist_get_song(c, new_index);

	/* send the move command to mpd */
#ifdef ENABLE_SONG_ID
	D("Swapping id:%d with id:%d\n", song1->id, song2->id);
	mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
#else
	D("Moving index %d to id:%d\n", old_index, new_index);
	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 songs position field */
	n = song1->pos;
	song1->pos = song2->pos;
	song2->pos = n;
	index1 = MIN(old_index, new_index);
	index2 = MAX(old_index, new_index);
	/* retreive the list items */
	item1 = g_list_nth(c->playlist.list, index1);
	item2 = g_list_nth(c->playlist.list, index2);
	/* retrieve the songs */
	data1 = item1->data;
	data2 = item2->data;

	/* move the second item */
	c->playlist.list = g_list_remove(c->playlist.list, data2);
	c->playlist.list = g_list_insert_before(c->playlist.list, item1, data2);

	/* move the first item */
	if (index2-index1 > 1) {
		item2 = g_list_nth(c->playlist.list, index2);
		c->playlist.list = g_list_remove(c->playlist.list, data1);
		c->playlist.list = g_list_insert_before(c->playlist.list,
							item2, data1);
	}

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

#else
	c->need_update = TRUE;
#endif

	/* call playlist updated callback */
	D("move> new_index=%d, old_index=%d\n", new_index, old_index);
	mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);

	return 0;
}

gint 
mpdclient_cmd_save_playlist_utf8(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_save_playlist(mpdclient_t *c, gchar *filename)
{
  gint retval = 0;
  gchar *filename_utf8 = locale_to_utf8(filename);
  
  retval = mpdclient_cmd_save_playlist_utf8(c, filename);
  g_free(filename_utf8);
  return retval;
}

gint 
mpdclient_cmd_load_playlist_utf8(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_utf8(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;
}

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

  retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
  g_free(filename_utf8);
  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 *******************************************/
/****************************************************************************/

gint
mpdclient_playlist_free(mpdclient_playlist_t *playlist)
{
  GList *list = g_list_first(playlist->list);

  while(list)
    {
      mpd_Song *song = (mpd_Song *) list->data;
      mpd_freeSong(song);
      list=list->next;
    }
  g_list_free(playlist->list);
	memset(playlist, 0, sizeof(mpdclient_playlist_t));
	return 0;
}

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

	D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);

	if (MPD_ERROR(c))
		return -1;

	if (c->playlist.list)
		mpdclient_playlist_free(&c->playlist);

	mpd_sendPlaylistInfoCommand(c->connection,-1);
	while ((entity = mpd_getNextInfoEntity(c->connection))) {
		if (entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
			mpd_Song *song = mpd_songDup(entity->info.song);

			c->playlist.list = g_list_append(c->playlist.list,
							 (gpointer)song);
			c->playlist.length++;
		}
		mpd_freeInfoEntity(entity);
	}

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

	/* 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;

	D("mpdclient_playlist_update_changes() [%lld -> %lld]\n",
	  c->status->playlist, c->playlist.id);

	if (MPD_ERROR(c))
		return -1;

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

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

		if (song->pos >= 0 && (guint)song->pos < c->playlist.length) {
			GList *item = g_list_nth(c->playlist.list, song->pos);

			/* update song */
			D("updating pos:%d, id=%d [%p] - %s\n",
			  song->pos, song->id, item, song->file);
			mpd_freeSong((mpd_Song *) item->data);
			item->data = mpd_songDup(song);
		} else {
			/* add a new song */
			D("adding song at pos %d\n", song->pos);
			c->playlist.list = g_list_append(c->playlist.list,
							 (gpointer)mpd_songDup(song));
		}
	}

	/* remove trailing songs */
	while ((guint)c->status->playlistLength < c->playlist.length) {
		GList *item = g_list_last(c->playlist.list);

		/* Remove the last playlist entry */
		D("removing song at pos %d\n", ((mpd_Song *) item->data)->pos);
		mpd_freeSong((mpd_Song *) item->data);
		c->playlist.list = g_list_delete_link(c->playlist.list, item);
		c->playlist.length = g_list_length(c->playlist.list);
	}

	c->song = NULL;
	c->playlist.id = c->status->playlist;
	c->playlist.updated = TRUE;
	c->playlist.length = g_list_length(c->playlist.list);

	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

mpd_Song *
playlist_get_song(mpdclient_t *c, gint idx)
{
	return (mpd_Song *) g_list_nth_data(c->playlist.list, idx);
}

GList *
playlist_lookup(mpdclient_t *c, int id)
{
	GList *list = g_list_first(c->playlist.list);

	while (list) {
		mpd_Song *song = (mpd_Song *) list->data;
		if( song->id == id )
			return list;
		list=list->next;
	}

	return NULL;
}

mpd_Song *
playlist_lookup_song(mpdclient_t *c, gint id)
{
	GList *list = c->playlist.list;

	while (list) {
		mpd_Song *song = (mpd_Song *) list->data;
		if (song->id == id)
			return song;
		list=list->next;
	}

	return NULL;
}

gint
playlist_get_index(mpdclient_t *c, mpd_Song *song)
{
	return g_list_index(c->playlist.list, song);
}

gint
playlist_get_index_from_id(mpdclient_t *c, gint id)
{
	return g_list_index(c->playlist.list, playlist_lookup_song(c, id));
}

gint
playlist_get_index_from_file(mpdclient_t *c, gchar *filename)
{
  GList *list = c->playlist.list;
  gint i=0;

  while( list )
    {
      mpd_Song *song = (mpd_Song *) list->data;
      if( strcmp(song->file, filename ) == 0 )	
	return i;
      list=list->next;
      i++;
    }
  return -1;
}


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

mpdclient_filelist_t *
mpdclient_filelist_free(mpdclient_filelist_t *filelist)
{
  GList *list = g_list_first(filelist->list);

  D("mpdclient_filelist_free()\n");
  if( list == NULL )
    return NULL;
  while( list!=NULL )
    {
      filelist_entry_t *entry = list->data;

      if( entry->entity )
	mpd_freeInfoEntity(entry->entity);
      g_free(entry);
      list=list->next;
    }
  g_list_free(filelist->list);
  g_free(filelist->path);
  filelist->path = NULL;
  filelist->list = NULL;
  filelist->length = 0;
  g_free(filelist);

  return NULL;
}


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

  D("mpdclient_filelist_get(%s)\n", path);
  mpd_sendLsInfoCommand(c->connection, path_utf8);
  filelist = g_malloc0(sizeof(mpdclient_filelist_t));
  if( path && path[0] && strcmp(path, "/") )
    {
      /* add a dummy entry for ./.. */
      filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
      entry->entity = NULL;
      filelist->list = g_list_append(filelist->list, (gpointer) entry);
      filelist->length++;
    }

  while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
    {
      filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
      
      entry->entity = entity;
      filelist->list = g_list_append(filelist->list, (gpointer) entry);
      filelist->length++;

      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);
  
  g_free(path_utf8);
  filelist->path = g_strdup(path);
  filelist->updated = TRUE;

  // If there are only directory entities in the filelist, we sort it
  if (has_dirs_only)
    {
      D("mpdclient_filelist_get: only dirs; sorting!\n");
      filelist->list = g_list_sort(filelist->list, compare_filelistentry_dir);
    }

  return filelist;
}

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

  D("mpdclient_filelist_search(%s)\n", filter_utf8);
  if( exact_match )
    mpd_sendFindCommand(c->connection, table, filter_utf8);
  else
    mpd_sendSearchCommand(c->connection, table, filter_utf8);
  filelist = g_malloc0(sizeof(mpdclient_filelist_t));

  while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
    {
      filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
      
      entry->entity = entity;
      filelist->list = g_list_append(filelist->list, (gpointer) entry);
      filelist->length++;
    }
  
  if( mpdclient_finish_command(c) )
    return mpdclient_filelist_free(filelist);

  filelist->updated = TRUE;

  return filelist;
}


mpdclient_filelist_t *
mpdclient_filelist_search(mpdclient_t *c,
			  int exact_match,
			  int table,
			  gchar *_filter)
{
	mpdclient_filelist_t *filelist;
	gchar *filter_utf8 = locale_to_utf8(_filter);

	D("mpdclient_filelist_search(%s)\n", _filter);
	filelist = mpdclient_filelist_search_utf8(c, exact_match, table,
						  filter_utf8);
	g_free(filter_utf8);

	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 = mpdclient_filelist_free(filelist);
      filelist = mpdclient_filelist_get(c, path);
      g_free(path);
      return filelist;
    }
  return NULL;
}

filelist_entry_t *
mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song)
{
  GList *list = g_list_first(fl->list);

  while( list && song)
    {
      filelist_entry_t *entry = list->data;
      mpd_InfoEntity *entity  = entry->entity;

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

	  if( strcmp(song->file, song2->file) == 0 )
	    {
	      return entry;
	    }
	}
      list = list->next;
    }
  return NULL;
}

int
mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
{
  GList *list = g_list_first(fl->list);

  if( fl->list==NULL || fl->length<1 )
    return 0;

  mpd_sendCommandListBegin(c->connection);
  while( list )
    {
      filelist_entry_t *entry = list->data;
      mpd_InfoEntity *entity  = entry->entity;

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

	  mpd_sendAddCommand(c->connection, song->file);
	}
      list = list->next;
    }
  mpd_sendCommandListEnd(c->connection);
  return mpdclient_finish_command(c);
}








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

  D("mpdclient_get_artists()\n");
  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_utf8(mpdclient_t *c, gchar *artist_utf8)
{
  gchar *str = NULL; 
  GList *list = NULL;

  D("mpdclient_get_albums(%s)\n", artist_utf8);
  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;
}