Code

Added exact_match parameter to mpdclient_filelist_search()
[ncmpc.git] / src / mpdclient.c
1 /* 
2  * $Id$
3  *
4  * (c) 2004 by Kalle Wallin <kaw@linux.se>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <time.h>
25 #include <string.h>
26 #include <glib.h>
28 #include "config.h"
29 #include "ncmpc.h"
30 #include "support.h"
31 #include "mpdclient.h"
32 #include "options.h"
34 #undef  ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
35 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
36 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
37 #define ENABLE_SONG_ID
38 #define ENABLE_PLCHANGES 
40 #define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)
42 /* from utils.c */
43 extern GList *string_list_free(GList *string_list);
45 /* Error callbacks */
46 static gint
47 error_cb(mpdclient_t *c, gint error, gchar *msg)
48 {
49   GList *list = c->error_callbacks;
50   
51   if( list==NULL )
52     fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
54   while(list)
55     {
56       mpdc_error_cb_t cb = list->data;
57       if( cb )
58         cb(c, error, msg);
59       list=list->next;
60     }
61   mpd_clearError(c->connection);
62   return error;
63 }
65 #ifdef DEBUG
66 #include "strfsong.h"
68 static gchar *
69 get_song_name(mpd_Song *song)
70 {
71   static gchar name[256];
73   strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
74   return name;
75 }
77 #endif
79 /****************************************************************************/
80 /*** mpdclient functions ****************************************************/
81 /****************************************************************************/
83 gint
84 mpdclient_finish_command(mpdclient_t *c) 
85 {
86   mpd_finishCommand(c->connection);
88   if( c->connection->error )
89     {
90       gchar *msg = locale_to_utf8(c->connection->errorStr);
91       gint error = c->connection->error;
92       
93       if( error == MPD_ERROR_ACK )
94         error = error | (c->connection->errorCode << 8);
96       error_cb(c, error, msg);
97       g_free(msg);
98       return error;
99     }
101   return 0;
104 mpdclient_t *
105 mpdclient_new(void)
107   mpdclient_t *c;
109   c = g_malloc0(sizeof(mpdclient_t));
111   return c;
114 mpdclient_t *
115 mpdclient_free(mpdclient_t *c)
117   mpdclient_disconnect(c);
118   g_list_free(c->error_callbacks);
119   g_list_free(c->playlist_callbacks);
120   g_list_free(c->browse_callbacks);
121   g_free(c);
123   return NULL;
126 gint
127 mpdclient_disconnect(mpdclient_t *c)
129   if( c->connection )
130     mpd_closeConnection(c->connection);
131   c->connection = NULL;
133   if( c->status )
134     mpd_freeStatus(c->status);
135   c->status = NULL;
137   if( c->playlist.list )
138     mpdclient_playlist_free(&c->playlist);
140   if( c->song )
141     c->song = NULL;
142   
143   return 0;
146 gint
147 mpdclient_connect(mpdclient_t *c, 
148                   gchar *host, 
149                   gint port, 
150                   gfloat timeout,
151                   gchar *password)
153   gint retval = 0;
154   
155   /* close any open connection */
156   if( c->connection )
157     mpdclient_disconnect(c);
159   /* connect to MPD */
160   c->connection = mpd_newConnection(host, port, timeout);
161   if( c->connection->error )
162     return error_cb(c, c->connection->error, c->connection->errorStr);
164   /* send password */
165   if( password )
166     {
167       mpd_sendPasswordCommand(c->connection, password);
168       retval = mpdclient_finish_command(c);
169     }
170   c->need_update = TRUE;
172   return retval;
175 gint
176 mpdclient_update(mpdclient_t *c)
178   gint retval = 0;
180   if( MPD_ERROR(c) )
181     return -1;
183   /* free the old status */
184   if( c->status )
185     mpd_freeStatus(c->status);
186   
187   /* retreive new status */
188   mpd_sendStatusCommand(c->connection);
189   c->status = mpd_getStatus(c->connection);
190   if( (retval=mpdclient_finish_command(c)) )
191     return retval;
192 #ifdef DEBUG
193   if( c->status->error )
194     D("status> %s\n", c->status->error);
195 #endif
197   /* check if the playlist needs an update */
198   if( c->playlist.id != c->status->playlist )
199     {
200       if( c->playlist.list )
201         retval = mpdclient_playlist_update_changes(c);
202       else
203         retval = mpdclient_playlist_update(c);
204     }
206   /* update the current song */
207   if( !c->song || c->status->songid != c->song->id )
208     {
209       c->song = playlist_get_song(c, c->status->song);
210     }
212   c->need_update = FALSE;
214   return retval;
218 /****************************************************************************/
219 /*** MPD Commands  **********************************************************/
220 /****************************************************************************/
222 gint 
223 mpdclient_cmd_play(mpdclient_t *c, gint index)
225 #ifdef ENABLE_SONG_ID
226   mpd_Song *song = playlist_get_song(c, index);
228   D("Play id:%d\n", song ? song->id : -1);
229   if( song )
230     mpd_sendPlayIdCommand(c->connection, song->id);
231   else
232     mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
233 #else
234   mpd_sendPlayCommand(c->connection, index);
235 #endif
236   c->need_update = TRUE;
237   return mpdclient_finish_command(c);
240 gint 
241 mpdclient_cmd_pause(mpdclient_t *c, gint value)
243   mpd_sendPauseCommand(c->connection, value);
244   return mpdclient_finish_command(c);
247 gint 
248 mpdclient_cmd_stop(mpdclient_t *c)
250   mpd_sendStopCommand(c->connection);
251   return mpdclient_finish_command(c);
254 gint 
255 mpdclient_cmd_next(mpdclient_t *c)
257   mpd_sendNextCommand(c->connection);
258   c->need_update = TRUE;
259   return mpdclient_finish_command(c);
262 gint 
263 mpdclient_cmd_prev(mpdclient_t *c)
265   mpd_sendPrevCommand(c->connection);
266   c->need_update = TRUE;
267   return mpdclient_finish_command(c);
270 gint 
271 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
273   D("Seek id:%d\n", id);
274   mpd_sendSeekIdCommand(c->connection, id, pos);
275   return mpdclient_finish_command(c);
278 gint 
279 mpdclient_cmd_shuffle(mpdclient_t *c)
281   mpd_sendShuffleCommand(c->connection);
282   c->need_update = TRUE;
283   return mpdclient_finish_command(c);
286 gint 
287 mpdclient_cmd_clear(mpdclient_t *c)
289   gint retval = 0;
291   mpd_sendClearCommand(c->connection);
292   retval = mpdclient_finish_command(c);
293   /* call playlist updated callback */
294   mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
295   c->need_update = TRUE;
296   return retval;
299 gint 
300 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
302   mpd_sendRepeatCommand(c->connection, value);
303   return mpdclient_finish_command(c);
306 gint 
307 mpdclient_cmd_random(mpdclient_t *c, gint value)
309   mpd_sendRandomCommand(c->connection, value);
310   return mpdclient_finish_command(c);
313 gint 
314 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
316   mpd_sendCrossfadeCommand(c->connection, value);
317   return mpdclient_finish_command(c);
320 gint 
321 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
323   mpd_sendUpdateCommand(c->connection, path ? path : "");
324   return mpdclient_finish_command(c);
327 gint 
328 mpdclient_cmd_volume(mpdclient_t *c, gint value)
330   mpd_sendSetvolCommand(c->connection, value);
331   return mpdclient_finish_command(c);
334 gint 
335 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
337   mpd_sendAddCommand(c->connection, path_utf8);
338   return mpdclient_finish_command(c);
341 gint 
342 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
344   gint retval;
345   gchar *path_utf8 = locale_to_utf8(path);
347   retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
348   g_free(path_utf8);
349   return retval;
352 gint 
353 mpdclient_cmd_add(mpdclient_t *c, mpd_Song *song)
354
355   gint retval = 0;
357   if( !song || !song->file )
358     return -1;
360   /* send the add command to mpd */
361   mpd_sendAddCommand(c->connection, song->file);
362   if( (retval=mpdclient_finish_command(c)) )
363     return retval;
365 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
366   /* add the song to playlist */
367   c->playlist.list = g_list_append(c->playlist.list, mpd_songDup(song));
368   c->playlist.length++;
370   /* increment the playlist id, so we dont retrives a new playlist */
371   c->playlist.id++;
373   /* call playlist updated callback */
374   mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
375 #else
376   c->need_update = TRUE;
377 #endif
379   return 0;
382 gint
383 mpdclient_cmd_delete(mpdclient_t *c, gint index)
385   gint retval = 0;
386   mpd_Song *song = playlist_get_song(c, index);
388   if( !song )
389     return -1;
391   /* send the delete command to mpd */
392 #ifdef ENABLE_SONG_ID
393   D("Delete id:%d\n", song->id);
394   mpd_sendDeleteIdCommand(c->connection, song->id);
395 #else
396   mpd_sendDeleteCommand(c->connection, index);
397 #endif
398   if( (retval=mpdclient_finish_command(c)) )
399     return retval;
401 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
402   /* increment the playlist id, so we dont retrive a new playlist */
403   c->playlist.id++;
405   /* remove the song from the playlist */
406   c->playlist.list = g_list_remove(c->playlist.list, (gpointer) song);
407   c->playlist.length = g_list_length(c->playlist.list);
409   /* call playlist updated callback */
410   mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
412   /* remove references to the song */
413   if( c->song == song )
414     {
415       c->song = NULL;   
416       c->need_update = TRUE;
417     }
419   /* free song */
420   mpd_freeSong(song);  
422 #else
423   c->need_update = TRUE;
424 #endif
426   return 0;
429 gint
430 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
432   gint n, index1, index2;
433   GList *item1, *item2;
434   gpointer data1, data2;
435   mpd_Song *song1, *song2;
437   if( old_index==new_index || new_index<0 || new_index>=c->playlist.length )
438     return -1;
440   song1 = playlist_get_song(c, old_index);
441   song2 = playlist_get_song(c, new_index);
443   /* send the move command to mpd */  
444 #ifdef ENABLE_SONG_ID
445   D("Swaping id:%d with id:%d\n", song1->id, song2->id);
446   mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
447 #else
448   D("Moving index %d to id:%d\n", old_index, new_index);
449   mpd_sendMoveCommand(c->connection, old_index, new_index);
450 #endif
451   if( (n=mpdclient_finish_command(c)) )
452     return n;
454 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
455   /* update the songs position field */
456   n = song1->pos;
457   song1->pos = song2->pos;
458   song2->pos = n;
459   index1 = MIN(old_index, new_index);
460   index2 = MAX(old_index, new_index);
461   /* retreive the list items */
462   item1 = g_list_nth(c->playlist.list, index1);
463   item2 = g_list_nth(c->playlist.list, index2);
464   /* retrieve the songs */
465   data1 = item1->data;
466   data2 = item2->data;
468   /* move the second item */
469   c->playlist.list = g_list_remove(c->playlist.list, data2);
470   c->playlist.list = g_list_insert_before(c->playlist.list, item1, data2);
472   /* move the first item */
473   if( index2-index1 >1 )
474     {
475       item2 = g_list_nth(c->playlist.list, index2);
476       c->playlist.list = g_list_remove(c->playlist.list, data1);
477       c->playlist.list = g_list_insert_before(c->playlist.list, item2, data1);
478     }
480   /* increment the playlist id, so we dont retrives a new playlist */
481   c->playlist.id++;
483 #else
484   c->need_update = TRUE;
485 #endif 
487   /* call playlist updated callback */
488   D("move> new_index=%d, old_index=%d\n", new_index, old_index);
489   mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
491   return 0;
494 gint 
495 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
497   gint retval = 0;
499   mpd_sendSaveCommand(c->connection, filename_utf8);
500   if( (retval=mpdclient_finish_command(c)) == 0 )
501     mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
502   return retval;
505 gint 
506 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
508   gint retval = 0;
509   gchar *filename_utf8 = locale_to_utf8(filename);
510   
511   retval = mpdclient_cmd_save_playlist_utf8(c, filename);
512   g_free(filename_utf8);
513   return retval;
516 gint 
517 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
519   mpd_sendLoadCommand(c->connection, filename_utf8);
520   c->need_update = TRUE;
521   return mpdclient_finish_command(c);
524 gint 
525 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
527   gint retval = 0;
529   mpd_sendRmCommand(c->connection, filename_utf8);
530   if( (retval=mpdclient_finish_command(c)) == 0 )
531     mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
532   return retval;
535 gint 
536 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
538   gint retval = 0;
539   gchar *filename_utf8 = locale_to_utf8(filename);
541   retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
542   g_free(filename_utf8);
543   return retval;
547 /****************************************************************************/
548 /*** Callback managment functions *******************************************/
549 /****************************************************************************/
550 static void
551 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
553   while(list)
554     {
555       mpdc_list_cb_t fn = list->data;
557       fn(c, event, data);
558       list=list->next;
559     }
562 void
563 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
565   do_list_callbacks(c, c->playlist_callbacks, event, data);
568 void
569 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
571   c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
574 void
575 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
577   c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
580 void
581 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
583   do_list_callbacks(c, c->browse_callbacks, event, data);
587 void
588 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
590   c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
593 void
594 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
596   c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
599 void
600 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
602   c->error_callbacks = g_list_append(c->error_callbacks, cb);
605 void
606 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
608   c->error_callbacks = g_list_remove(c->error_callbacks, cb);
611 /****************************************************************************/
612 /*** Playlist managment functions *******************************************/
613 /****************************************************************************/
615 gint
616 mpdclient_playlist_free(mpdclient_playlist_t *playlist)
618   GList *list = g_list_first(playlist->list);
620   while(list)
621     {
622       mpd_Song *song = (mpd_Song *) list->data;
623       mpd_freeSong(song);
624       list=list->next;
625     }
626   g_list_free(playlist->list);
627   memset(playlist, 0, sizeof(mpdclient_playlist_t));
628   return 0;
631 /* update playlist */
632 gint 
633 mpdclient_playlist_update(mpdclient_t *c)
635   mpd_InfoEntity *entity;
637   D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
639   if( MPD_ERROR(c) )
640     return -1;
642   if( c->playlist.list )
643     mpdclient_playlist_free(&c->playlist);
645   mpd_sendPlaylistInfoCommand(c->connection,-1);
646   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
647     {
648       if(entity->type==MPD_INFO_ENTITY_TYPE_SONG) 
649         {
650           mpd_Song *song = mpd_songDup(entity->info.song);
652           c->playlist.list = g_list_append(c->playlist.list, (gpointer) song);
653           c->playlist.length++;
654         }
655       mpd_freeInfoEntity(entity);
656     }
657   c->playlist.id = c->status->playlist;
658   c->song = NULL;
659   c->playlist.updated = TRUE;
661   /* call playlist updated callbacks */
662   mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
664   return mpdclient_finish_command(c);
667 #ifdef ENABLE_PLCHANGES
669 gint 
670 mpdclient_compare_songs(gconstpointer a, gconstpointer b)
672   mpd_Song *song1 = (mpd_Song *) a; 
673   mpd_Song *song2 = (mpd_Song *) b; 
675   return song1->pos - song2->pos;
680 /* update playlist (plchanges) */
681 gint 
682 mpdclient_playlist_update_changes(mpdclient_t *c)
684   mpd_InfoEntity *entity;
686   D("mpdclient_playlist_update_changes() [%lld -> %lld]\n", 
687     c->status->playlist, c->playlist.id);
689   if( MPD_ERROR(c) )
690     return -1;
692   mpd_sendPlChangesCommand(c->connection, c->playlist.id); 
694   while( (entity=mpd_getNextInfoEntity(c->connection)) != NULL   ) 
695     {
696       mpd_Song *song = entity->info.song;
698       if( song->pos < c->playlist.length )
699         {
700           GList *item = g_list_nth(c->playlist.list, song->pos);
702           /* update song */
703           D("updating pos:%d, id=%d [%p] - %s\n", 
704             song->pos, song->id, item, song->file);
705           mpd_freeSong((mpd_Song *) item->data);                     
706           item->data = mpd_songDup(song);
707         }
708       else
709         {
710           /* add a new song */
711           D("adding song at pos %d\n", song->pos);
712           c->playlist.list = g_list_append(c->playlist.list, 
713                                            (gpointer) mpd_songDup(song));
714         }
715       
716     }
718   /* remove trailing songs */
719   while( c->status->playlistLength < c->playlist.length )
720     {
721       GList *item = g_list_last(c->playlist.list);
723       /* Remove the last playlist entry */
724       D("removing song at pos %d\n", ((mpd_Song *) item->data)->pos);
725       mpd_freeSong((mpd_Song *) item->data);
726       c->playlist.list = g_list_delete_link(c->playlist.list, item);
727       c->playlist.length = g_list_length(c->playlist.list);   
728     }
730   c->song = NULL;
731   c->playlist.id = c->status->playlist;
732   c->playlist.updated = TRUE;
733   c->playlist.length = g_list_length(c->playlist.list);
735   mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
737   return 0;
740 #else
741 gint 
742 mpdclient_playlist_update_changes(mpdclient_t *c)
744   return mpdclient_playlist_update(c);
746 #endif
748 mpd_Song *
749 playlist_get_song(mpdclient_t *c, gint index)
751   return (mpd_Song *) g_list_nth_data(c->playlist.list, index);
754 GList *
755 playlist_lookup(mpdclient_t *c, int id)
757   GList *list = g_list_first(c->playlist.list);
759   while( list )
760     {
761       mpd_Song *song = (mpd_Song *) list->data;
762       if( song->id == id )
763         return list;
764       list=list->next;
765     }
766   return NULL;
769 mpd_Song *
770 playlist_lookup_song(mpdclient_t *c, gint id)
772   GList *list = c->playlist.list;
774   while( list )
775     {
776       mpd_Song *song = (mpd_Song *) list->data;
777       if( song->id == id )
778         return song;
779       list=list->next;
780     }
781   return NULL;
784 gint 
785 playlist_get_index(mpdclient_t *c, mpd_Song *song)
787   return g_list_index(c->playlist.list, song);
790 gint 
791 playlist_get_index_from_id(mpdclient_t *c, gint id)
793   return g_list_index(c->playlist.list, playlist_lookup_song(c, id));
796 gint
797 playlist_get_index_from_file(mpdclient_t *c, gchar *filename)
799   GList *list = c->playlist.list;
800   gint i=0;
802   while( list )
803     {
804       mpd_Song *song = (mpd_Song *) list->data;
805       if( strcmp(song->file, filename ) == 0 )  
806         return i;
807       list=list->next;
808       i++;
809     }
810   return -1;
814 /****************************************************************************/
815 /*** Filelist functions *****************************************************/
816 /****************************************************************************/
818 mpdclient_filelist_t *
819 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
821   GList *list = g_list_first(filelist->list);
823   D("mpdclient_filelist_free()\n");
824   if( list == NULL )
825     return NULL;
826   while( list!=NULL )
827     {
828       filelist_entry_t *entry = list->data;
830       if( entry->entity )
831         mpd_freeInfoEntity(entry->entity);
832       g_free(entry);
833       list=list->next;
834     }
835   g_list_free(filelist->list);
836   g_free(filelist->path);
837   filelist->path = NULL;
838   filelist->list = NULL;
839   filelist->length = 0;
840   g_free(filelist);
842   return NULL;
846 mpdclient_filelist_t *
847 mpdclient_filelist_get(mpdclient_t *c, gchar *path)
849   mpdclient_filelist_t *filelist;
850   mpd_InfoEntity *entity;
851   gchar *path_utf8 = locale_to_utf8(path);
853   D("mpdclient_filelist_get(%s)\n", path);
854   mpd_sendLsInfoCommand(c->connection, path_utf8);
855   filelist = g_malloc0(sizeof(mpdclient_filelist_t));
856   if( path && path[0] && strcmp(path, "/") )
857     {
858       /* add a dummy entry for ./.. */
859       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
860       entry->entity = NULL;
861       filelist->list = g_list_append(filelist->list, (gpointer) entry);
862       filelist->length++;
863     }
865   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
866     {
867       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
868       
869       entry->entity = entity;
870       filelist->list = g_list_append(filelist->list, (gpointer) entry);
871       filelist->length++;
872     }
873   
874   if( mpdclient_finish_command(c) )
875     {
876       g_free(path_utf8);
877       return mpdclient_filelist_free(filelist);
878     }
879   
880   g_free(path_utf8);
881   filelist->path = g_strdup(path);
882   filelist->updated = TRUE;
884   return filelist;
887 mpdclient_filelist_t *
888 mpdclient_filelist_search_utf8(mpdclient_t *c, 
889                                int exact_match,
890                                int table, 
891                                gchar *filter_utf8)
893   mpdclient_filelist_t *filelist;
894   mpd_InfoEntity *entity;
896   D("mpdclient_filelist_search(%s)\n", filter_utf8);
897   if( exact_match )
898     mpd_sendFindCommand(c->connection, table, filter_utf8);
899   else
900     mpd_sendSearchCommand(c->connection, table, filter_utf8);
901   filelist = g_malloc0(sizeof(mpdclient_filelist_t));
903   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
904     {
905       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
906       
907       entry->entity = entity;
908       filelist->list = g_list_append(filelist->list, (gpointer) entry);
909       filelist->length++;
910     }
911   
912   if( mpdclient_finish_command(c) )
913     return mpdclient_filelist_free(filelist);
915   filelist->updated = TRUE;
917   return filelist;
921 mpdclient_filelist_t *
922 mpdclient_filelist_search(mpdclient_t *c,
923                           int exact_match, 
924                           int table, 
925                           gchar *filter)
927   mpdclient_filelist_t *filelist;
928   gchar *filter_utf8 = locale_to_utf8(filter);
930   D("mpdclient_filelist_search(%s)\n", filter);
931   filelist = mpdclient_filelist_search_utf8(c,exact_match,table,filter_utf8);
932   g_free(filter_utf8);
934   return filelist;
937 mpdclient_filelist_t *
938 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
940   if( filelist != NULL )
941     {    
942       gchar *path = g_strdup(filelist->path);
944       filelist = mpdclient_filelist_free(filelist);
945       filelist = mpdclient_filelist_get(c, path);
946       g_free(path);
947       return filelist;
948     }
949   return NULL;
952 filelist_entry_t *
953 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song)
955   GList *list = g_list_first(fl->list);
957   while( list && song)
958     {
959       filelist_entry_t *entry = list->data;
960       mpd_InfoEntity *entity  = entry->entity;
962       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
963         {
964           mpd_Song *song2 = entity->info.song;
966           if( strcmp(song->file, song2->file) == 0 )
967             {
968               return entry;
969             }
970         }
971       list = list->next;
972     }
973   return NULL;
976 int
977 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
979   GList *list = g_list_first(fl->list);
981   if( fl->list==NULL || fl->length<1 )
982     return 0;
984   mpd_sendCommandListBegin(c->connection);
985   while( list )
986     {
987       filelist_entry_t *entry = list->data;
988       mpd_InfoEntity *entity  = entry->entity;
990       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
991         {
992           mpd_Song *song = entity->info.song;
994           mpd_sendAddCommand(c->connection, song->file);
995         }
996       list = list->next;
997     }
998   mpd_sendCommandListEnd(c->connection);
999   return mpdclient_finish_command(c);
1009 GList *
1010 mpdclient_get_artists_utf8(mpdclient_t *c)
1012   gchar *str = NULL; 
1013   GList *list = NULL;
1015   D("mpdclient_get_artists()\n");
1016   mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
1017   while( (str=mpd_getNextArtist(c->connection)) )
1018     {
1019       list = g_list_append(list, (gpointer) str);
1020     }
1021   if( mpdclient_finish_command(c) )
1022     {
1023       return string_list_free(list);
1024     }  
1026   return list;
1029 GList *
1030 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
1032   gchar *str = NULL; 
1033   GList *list = NULL;
1035   D("mpdclient_get_albums(%s)\n", artist_utf8);
1036   mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
1037   while( (str=mpd_getNextAlbum(c->connection)) )
1038     {
1039       list = g_list_append(list, (gpointer) str);
1040     }
1041   if( mpdclient_finish_command(c) )
1042     {
1043       return string_list_free(list);
1044     }  
1045   
1046   return list;