aboutsummaryrefslogblamecommitdiffstats
path: root/src/mpdclient.c
blob: 1069f8ef539c914cc8605aeeee6f124756389be1 (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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <glib.h>

#include "config.h"
#include "ncmpc.h"
#include "support.h"
#include "mpdclient.h"
#include "options.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 MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)


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

#ifdef DEBUG
#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);

      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;
#ifdef DEBUG
  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 index)
{
#ifdef ENABLE_SONG_ID
  mpd_Song *song = playlist_get_song(c, index);

  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, index);
#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(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)
{
  gint retval;
  gchar *path_utf8 = locale_to_utf8(path);

  mpd_sendAddCommand(c->connection, path_utf8);
  retval=mpdclient_finish_command(c);
  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 index)
{
  gint retval = 0;
  mpd_Song *song = playlist_get_song(c, index);

  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, index);
#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 || 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("Swaping 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 */
  mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);

  return 0;
}

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

  mpd_sendSaveCommand(c->connection, filename_utf8);
  if( (retval=mpdclient_finish_command(c)) == 0 )
    mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
  g_free(filename_utf8);
  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 *******************************************/
/****************************************************************************/

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

gint 
mpdclient_compare_songs(gconstpointer a, gconstpointer b)
{
  mpd_Song *song1 = (mpd_Song *) a; 
  mpd_Song *song2 = (mpd_Song *) b; 

  return song1->pos - song2->pos;
}



/* 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   ) 
    {

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

	  item =  playlist_lookup(c, song->id);

#ifdef DEBUG	  
	  if( item )
	    D("Changing index:%d, pos:%d, id:%d => %s\n",
	      g_list_position(c->playlist.list, item), 
	      song->pos, song->id, get_song_name(song));
	  else
	    D("Unable to find pos:%d, id:%d => %s\n",
	      song->pos, song->id, get_song_name(song));
#endif

	  if( item && item->data)
	    {
	      GList *old;
	      gint index = g_list_position(c->playlist.list, item);

	      /* remove previous song at song->pos */
	      while( song->pos != (index=g_list_position(c->playlist.list, item))
		     && 
		     (old=g_list_nth(c->playlist.list, song->pos)) )
		{
		  D("Removing item with index %d id:%d (%d)\n", 
		    song->pos, old ? ((mpd_Song *) old->data)->id : -1, index);
		  if( item->data == c->song )
		    c->song = NULL;
		  mpd_freeSong((mpd_Song *) old->data);
		  old->data = NULL;
		  c->playlist.list = g_list_delete_link(c->playlist.list, old);
		  c->playlist.length = g_list_length(c->playlist.list);
		}

	      /* Update playlist entry */
	      mpd_freeSong((mpd_Song *) item->data);	      	     
	      item->data = mpd_songDup(song);
	      if( c->song && c->song->id == song->id )
		c->song = item->data;

	    }
	  else
	    {
	      /* Add a new  playlist entry */
	      D("Adding pos:%d, id;%d - %s\n",
		song->pos, song->id, get_song_name(song));
	      c->playlist.list = g_list_append(c->playlist.list, 
					       (gpointer) mpd_songDup(song));
	      c->playlist.length++;
	    }
	}
      mpd_freeInfoEntity(entity);      
    }
  mpd_finishCommand(c->connection);
  
  while( g_list_length(c->playlist.list) > c->status->playlistLength )
    {
      GList *item = g_list_last(c->playlist.list);

      /* Remove the last playlist entry */
      mpd_freeSong((mpd_Song *) item->data);
      c->playlist.list = g_list_delete_link(c->playlist.list, item);
      c->playlist.length--;      
      D("Removed the last playlist entry\n");
    }

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

  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 index)
{
  return (mpd_Song *) g_list_nth_data(c->playlist.list, index);
}

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");
  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, gchar *path)
{
  mpdclient_filelist_t *filelist;
  mpd_InfoEntity *entity;
  gchar *path_utf8 = locale_to_utf8(path);

  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( mpdclient_finish_command(c) )
    {
      g_free(path_utf8);
      return mpdclient_filelist_free(filelist);
    }
  
  g_free(path_utf8);
  filelist->path = g_strdup(path);
  filelist->updated = TRUE;

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