Code

playlist: use playlist_get() instead of playlist_get_song()
[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 "mpdclient.h"
22 #include "screen_utils.h"
23 #include "config.h"
24 #include "ncmpc.h"
25 #include "support.h"
26 #include "options.h"
27 #include "strfsong.h"
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <string.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 BUFSIZE 1024
42 #define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)
44 /* from utils.c */
45 extern GList *string_list_free(GList *string_list);
48 /* filelist sorting functions */
49 static gint
50 compare_filelistentry_dir(gconstpointer filelist_entry1,
51                           gconstpointer filelist_entry2)
52 {
53         const mpd_InfoEntity *e1, *e2;
54         char *key1, *key2;
55         int n = 0;
57         e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
58         e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
60         if (e1 && e2 &&
61             e1->type == MPD_INFO_ENTITY_TYPE_DIRECTORY &&
62             e2->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
63                 key1 = g_utf8_collate_key(e1->info.directory->path,-1);
64                 key2 = g_utf8_collate_key(e2->info.directory->path,-1);
65                 n = strcmp(key1,key2);
66                 g_free(key1);
67                 g_free(key2);
68         }
70         return n;
71 }
73 /* sort by list-format */
74 gint
75 compare_filelistentry_format(gconstpointer filelist_entry1,
76                              gconstpointer filelist_entry2)
77 {
78         const mpd_InfoEntity *e1, *e2;
79         char key1[BUFSIZE], key2[BUFSIZE];
80         int n = 0;
82         e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
83         e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
85         if (e1 && e2 &&
86             e1->type == MPD_INFO_ENTITY_TYPE_SONG &&
87             e2->type == MPD_INFO_ENTITY_TYPE_SONG) {
88                 strfsong(key1, BUFSIZE, LIST_FORMAT, e1->info.song);
89                 strfsong(key2, BUFSIZE, LIST_FORMAT, e2->info.song);
90                 n = strcmp(key1,key2);
91         }
93         return n;
94 }
97 /* Error callbacks */
98 static gint
99 error_cb(mpdclient_t *c, gint error, gchar *msg)
101   GList *list = c->error_callbacks;
102   
103   if( list==NULL )
104     fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
106   while(list)
107     {
108       mpdc_error_cb_t cb = list->data;
109       if( cb )
110         cb(c, error, msg);
111       list=list->next;
112     }
113   mpd_clearError(c->connection);
114   return error;
117 #ifndef NDEBUG
118 // Unused ath the moment
119 /*
120 #include "strfsong.h"
122 static gchar *
123 get_song_name(mpd_Song *song)
125   static gchar name[256];
127   strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
128   return name;
130 */
131 #endif
133 /****************************************************************************/
134 /*** mpdclient functions ****************************************************/
135 /****************************************************************************/
137 gint
138 mpdclient_finish_command(mpdclient_t *c) 
140   mpd_finishCommand(c->connection);
142   if( c->connection->error )
143     {
144       gchar *msg = locale_to_utf8(c->connection->errorStr);
145       gint error = c->connection->error;
146       if( error == MPD_ERROR_ACK )
147         error = error | (c->connection->errorCode << 8);
148       if(  c->connection->errorCode == MPD_ACK_ERROR_PERMISSION )
149         {
150           if(screen_auth(c) == 0) return 0;
151         }
152       error_cb(c, error, msg);
153       g_free(msg);
154       return error;
155     }
157   return 0;
160 mpdclient_t *
161 mpdclient_new(void)
163         mpdclient_t *c;
165         c = g_malloc0(sizeof(mpdclient_t));
166         playlist_init(&c->playlist);
168         return c;
171 mpdclient_t *
172 mpdclient_free(mpdclient_t *c)
174         mpdclient_disconnect(c);
176         mpdclient_playlist_free(&c->playlist);
178         g_list_free(c->error_callbacks);
179         g_list_free(c->playlist_callbacks);
180         g_list_free(c->browse_callbacks);
181         g_free(c);
183         return NULL;
186 gint
187 mpdclient_disconnect(mpdclient_t *c)
189         if (c->connection)
190                 mpd_closeConnection(c->connection);
191         c->connection = NULL;
193         if (c->status)
194                 mpd_freeStatus(c->status);
195         c->status = NULL;
197         playlist_clear(&c->playlist);
199         if (c->song)
200                 c->song = NULL;
202         return 0;
205 gint
206 mpdclient_connect(mpdclient_t *c,
207                   gchar *host,
208                   gint port,
209                   gfloat _timeout,
210                   gchar *password)
212         gint retval = 0;
214         /* close any open connection */
215         if( c->connection )
216                 mpdclient_disconnect(c);
218         /* connect to MPD */
219         c->connection = mpd_newConnection(host, port, _timeout);
220         if( c->connection->error )
221                 return error_cb(c, c->connection->error,
222                                 c->connection->errorStr);
224         /* send password */
225         if( password ) {
226                 mpd_sendPasswordCommand(c->connection, password);
227                 retval = mpdclient_finish_command(c);
228         }
229         c->need_update = TRUE;
231         return retval;
234 gint
235 mpdclient_update(mpdclient_t *c)
237         gint retval = 0;
239         if (MPD_ERROR(c))
240                 return -1;
242         /* free the old status */
243         if (c->status)
244                 mpd_freeStatus(c->status);
246         /* retreive new status */
247         mpd_sendStatusCommand(c->connection);
248         c->status = mpd_getStatus(c->connection);
249         if ((retval=mpdclient_finish_command(c)))
250                 return retval;
251 #ifndef NDEBUG
252         if (c->status->error)
253                 D("status> %s\n", c->status->error);
254 #endif
256         /* check if the playlist needs an update */
257         if (c->playlist.id != c->status->playlist) {
258                 if (playlist_is_empty(&c->playlist))
259                         retval = mpdclient_playlist_update_changes(c);
260                 else
261                         retval = mpdclient_playlist_update(c);
262         }
264         /* update the current song */
265         if (!c->song || c->status->songid != c->song->id) {
266                 c->song = playlist_get_song(c, c->status->song);
267         }
269         c->need_update = FALSE;
271         return retval;
275 /****************************************************************************/
276 /*** MPD Commands  **********************************************************/
277 /****************************************************************************/
279 gint
280 mpdclient_cmd_play(mpdclient_t *c, gint idx)
282 #ifdef ENABLE_SONG_ID
283         struct mpd_song *song = playlist_get_song(c, idx);
285         D("Play id:%d\n", song ? song->id : -1);
286         if (song)
287                 mpd_sendPlayIdCommand(c->connection, song->id);
288         else
289                 mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
290 #else
291         mpd_sendPlayCommand(c->connection, idx);
292 #endif
293         c->need_update = TRUE;
294         return mpdclient_finish_command(c);
297 gint
298 mpdclient_cmd_pause(mpdclient_t *c, gint value)
300         mpd_sendPauseCommand(c->connection, value);
301         return mpdclient_finish_command(c);
304 gint
305 mpdclient_cmd_stop(mpdclient_t *c)
307         mpd_sendStopCommand(c->connection);
308         return mpdclient_finish_command(c);
311 gint
312 mpdclient_cmd_next(mpdclient_t *c)
314         mpd_sendNextCommand(c->connection);
315         c->need_update = TRUE;
316         return mpdclient_finish_command(c);
319 gint
320 mpdclient_cmd_prev(mpdclient_t *c)
322         mpd_sendPrevCommand(c->connection);
323         c->need_update = TRUE;
324         return mpdclient_finish_command(c);
327 gint 
328 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
330   D("Seek id:%d\n", id);
331   mpd_sendSeekIdCommand(c->connection, id, pos);
332   return mpdclient_finish_command(c);
335 gint 
336 mpdclient_cmd_shuffle(mpdclient_t *c)
338   mpd_sendShuffleCommand(c->connection);
339   c->need_update = TRUE;
340   return mpdclient_finish_command(c);
343 gint 
344 mpdclient_cmd_clear(mpdclient_t *c)
346   gint retval = 0;
348   mpd_sendClearCommand(c->connection);
349   retval = mpdclient_finish_command(c);
350   /* call playlist updated callback */
351   mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
352   c->need_update = TRUE;
353   return retval;
356 gint 
357 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
359   mpd_sendRepeatCommand(c->connection, value);
360   return mpdclient_finish_command(c);
363 gint 
364 mpdclient_cmd_random(mpdclient_t *c, gint value)
366   mpd_sendRandomCommand(c->connection, value);
367   return mpdclient_finish_command(c);
370 gint 
371 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
373   mpd_sendCrossfadeCommand(c->connection, value);
374   return mpdclient_finish_command(c);
377 gint 
378 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
380   mpd_sendUpdateCommand(c->connection, path ? path : "");
381   return mpdclient_finish_command(c);
384 gint 
385 mpdclient_cmd_volume(mpdclient_t *c, gint value)
387   mpd_sendSetvolCommand(c->connection, value);
388   return mpdclient_finish_command(c);
391 gint 
392 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
394   mpd_sendAddCommand(c->connection, path_utf8);
395   return mpdclient_finish_command(c);
398 gint 
399 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
401   gint retval;
402   gchar *path_utf8 = locale_to_utf8(path);
404   retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
405   g_free(path_utf8);
406   return retval;
409 gint
410 mpdclient_cmd_add(mpdclient_t *c, struct mpd_song *song)
412         gint retval = 0;
414         if( !song || !song->file )
415                 return -1;
417         /* send the add command to mpd */
418         mpd_sendAddCommand(c->connection, song->file);
419         if( (retval=mpdclient_finish_command(c)) )
420                 return retval;
422 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
423         /* add the song to playlist */
424         playlist_append(&c->playlist, song);
426         /* increment the playlist id, so we dont retrives a new playlist */
427         c->playlist.id++;
429         /* call playlist updated callback */
430         mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
431 #else
432         c->need_update = TRUE;
433 #endif
435         return 0;
438 gint
439 mpdclient_cmd_delete(mpdclient_t *c, gint idx)
441         gint retval = 0;
442         struct mpd_song *song;
444         if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
445                 return -1;
447         song = playlist_get(&c->playlist, idx);
449         /* send the delete command to mpd */
450 #ifdef ENABLE_SONG_ID
451         D("Delete id:%d\n", song->id);
452         mpd_sendDeleteIdCommand(c->connection, song->id);
453 #else
454         mpd_sendDeleteCommand(c->connection, idx);
455 #endif
456         if( (retval=mpdclient_finish_command(c)) )
457                 return retval;
459 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
460         /* increment the playlist id, so we dont retrive a new playlist */
461         c->playlist.id++;
463         /* remove the song from the playlist */
464         playlist_remove(&c->playlist, idx);
466         /* call playlist updated callback */
467         mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
469         /* remove references to the song */
470         if (c->song == song) {
471                 c->song = NULL;
472                 c->need_update = TRUE;
473         }
475 #else
476         c->need_update = TRUE;
477 #endif
479         return 0;
482 gint
483 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
485         gint n;
486         struct mpd_song *song1, *song2;
488         if (old_index == new_index || new_index < 0 ||
489             (guint)new_index >= c->playlist.list->len)
490                 return -1;
492         song1 = playlist_get(&c->playlist, old_index);
493         song2 = playlist_get(&c->playlist, new_index);
495         /* send the move command to mpd */
496 #ifdef ENABLE_SONG_ID
497         D("Swapping id:%d with id:%d\n", song1->id, song2->id);
498         mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
499 #else
500         D("Moving index %d to id:%d\n", old_index, new_index);
501         mpd_sendMoveCommand(c->connection, old_index, new_index);
502 #endif
503         if( (n=mpdclient_finish_command(c)) )
504                 return n;
506 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
507         /* update the playlist */
508         playlist_swap(&c->playlist, old_index, new_index);
510         /* increment the playlist id, so we dont retrives a new playlist */
511         c->playlist.id++;
513 #else
514         c->need_update = TRUE;
515 #endif
517         /* call playlist updated callback */
518         D("move> new_index=%d, old_index=%d\n", new_index, old_index);
519         mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
521         return 0;
524 gint 
525 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
527   gint retval = 0;
529   mpd_sendSaveCommand(c->connection, filename_utf8);
530   if( (retval=mpdclient_finish_command(c)) == 0 )
531     mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
532   return retval;
535 gint 
536 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
538   gint retval = 0;
539   gchar *filename_utf8 = locale_to_utf8(filename);
540   
541   retval = mpdclient_cmd_save_playlist_utf8(c, filename);
542   g_free(filename_utf8);
543   return retval;
546 gint 
547 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
549   mpd_sendLoadCommand(c->connection, filename_utf8);
550   c->need_update = TRUE;
551   return mpdclient_finish_command(c);
554 gint 
555 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
557   gint retval = 0;
559   mpd_sendRmCommand(c->connection, filename_utf8);
560   if( (retval=mpdclient_finish_command(c)) == 0 )
561     mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
562   return retval;
565 gint 
566 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
568   gint retval = 0;
569   gchar *filename_utf8 = locale_to_utf8(filename);
571   retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
572   g_free(filename_utf8);
573   return retval;
577 /****************************************************************************/
578 /*** Callback managment functions *******************************************/
579 /****************************************************************************/
580 static void
581 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
583   while(list)
584     {
585       mpdc_list_cb_t fn = list->data;
587       fn(c, event, data);
588       list=list->next;
589     }
592 void
593 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
595   do_list_callbacks(c, c->playlist_callbacks, event, data);
598 void
599 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
601   c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
604 void
605 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
607   c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
610 void
611 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
613   do_list_callbacks(c, c->browse_callbacks, event, data);
617 void
618 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
620   c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
623 void
624 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
626   c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
629 void
630 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
632   c->error_callbacks = g_list_append(c->error_callbacks, cb);
635 void
636 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
638   c->error_callbacks = g_list_remove(c->error_callbacks, cb);
641 /****************************************************************************/
642 /*** Playlist managment functions *******************************************/
643 /****************************************************************************/
646 /* update playlist */
647 gint
648 mpdclient_playlist_update(mpdclient_t *c)
650         mpd_InfoEntity *entity;
652         D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
654         if (MPD_ERROR(c))
655                 return -1;
657         playlist_clear(&c->playlist);
659         mpd_sendPlaylistInfoCommand(c->connection,-1);
660         while ((entity = mpd_getNextInfoEntity(c->connection))) {
661                 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
662                         playlist_append(&c->playlist, entity->info.song);
664                 mpd_freeInfoEntity(entity);
665         }
667         c->playlist.id = c->status->playlist;
668         c->song = NULL;
669         c->playlist.updated = TRUE;
671         /* call playlist updated callbacks */
672         mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
674         return mpdclient_finish_command(c);
677 #ifdef ENABLE_PLCHANGES
679 /* update playlist (plchanges) */
680 gint
681 mpdclient_playlist_update_changes(mpdclient_t *c)
683         mpd_InfoEntity *entity;
685         D("mpdclient_playlist_update_changes() [%lld -> %lld]\n",
686           c->status->playlist, c->playlist.id);
688         if (MPD_ERROR(c))
689                 return -1;
691         mpd_sendPlChangesCommand(c->connection, c->playlist.id);
693         while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) {
694                 struct mpd_song *song = entity->info.song;
696                 if (song->pos >= 0 && (guint)song->pos < c->playlist.list->len) {
697                         /* update song */
698                         D("updating pos:%d, id=%d - %s\n",
699                           song->pos, song->id, song->file);
700                         playlist_replace(&c->playlist, song->pos, song);
701                 } else {
702                         /* add a new song */
703                         D("adding song at pos %d\n", song->pos);
704                         playlist_append(&c->playlist, song);
705                 }
707                 mpd_freeInfoEntity(entity);
708         }
710         /* remove trailing songs */
711         while ((guint)c->status->playlistLength < c->playlist.list->len) {
712                 guint pos = c->playlist.list->len - 1;
714                 /* Remove the last playlist entry */
715                 D("removing song at pos %d\n", pos);
716                 playlist_remove(&c->playlist, pos);
717         }
719         c->song = NULL;
720         c->playlist.id = c->status->playlist;
721         c->playlist.updated = TRUE;
723         mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
725         return 0;
728 #else
729 gint
730 mpdclient_playlist_update_changes(mpdclient_t *c)
732         return mpdclient_playlist_update(c);
734 #endif
737 /****************************************************************************/
738 /*** Filelist functions *****************************************************/
739 /****************************************************************************/
741 mpdclient_filelist_t *
742 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
744         GList *list = g_list_first(filelist->list);
746         D("mpdclient_filelist_free()\n");
747         if (list == NULL)
748                 return NULL;
749         while (list != NULL) {
750                 filelist_entry_t *entry = list->data;
752                 if (entry->entity)
753                         mpd_freeInfoEntity(entry->entity);
754                 g_free(entry);
755                 list=list->next;
756         }
757         g_list_free(filelist->list);
758         g_free(filelist->path);
759         filelist->path = NULL;
760         filelist->list = NULL;
761         filelist->length = 0;
762         g_free(filelist);
764         return NULL;
768 mpdclient_filelist_t *
769 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
771         mpdclient_filelist_t *filelist;
772         mpd_InfoEntity *entity;
773         gchar *path_utf8 = locale_to_utf8(path);
774         gboolean has_dirs_only = TRUE;
776         D("mpdclient_filelist_get(%s)\n", path);
777         mpd_sendLsInfoCommand(c->connection, path_utf8);
778         filelist = g_malloc0(sizeof(mpdclient_filelist_t));
779         if (path && path[0] && strcmp(path, "/")) {
780                 /* add a dummy entry for ./.. */
781                 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
782                 entry->entity = NULL;
783                 filelist->list = g_list_append(filelist->list, entry);
784                 filelist->length++;
785         }
787         while ((entity=mpd_getNextInfoEntity(c->connection))) {
788                 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
790                 entry->entity = entity;
791                 filelist->list = g_list_append(filelist->list, entry);
792                 filelist->length++;
794                 if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY) {
795                         has_dirs_only = FALSE;
796                 }
797         }
799         /* If there's an error, ignore it.  We'll return an empty filelist. */
800         mpdclient_finish_command(c);
802         g_free(path_utf8);
803         filelist->path = g_strdup(path);
804         filelist->updated = TRUE;
806         // If there are only directory entities in the filelist, we sort it
807         if (has_dirs_only) {
808                 D("mpdclient_filelist_get: only dirs; sorting!\n");
809                 filelist->list = g_list_sort(filelist->list, compare_filelistentry_dir);
810         }
812         return filelist;
815 mpdclient_filelist_t *
816 mpdclient_filelist_search_utf8(mpdclient_t *c,
817                                int exact_match,
818                                int table,
819                                gchar *filter_utf8)
821         mpdclient_filelist_t *filelist;
822         mpd_InfoEntity *entity;
824         D("mpdclient_filelist_search(%s)\n", filter_utf8);
825         if (exact_match)
826                 mpd_sendFindCommand(c->connection, table, filter_utf8);
827         else
828                 mpd_sendSearchCommand(c->connection, table, filter_utf8);
829         filelist = g_malloc0(sizeof(mpdclient_filelist_t));
831         while ((entity=mpd_getNextInfoEntity(c->connection))) {
832                 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
834                 entry->entity = entity;
835                 filelist->list = g_list_append(filelist->list, entry);
836                 filelist->length++;
837         }
839         if (mpdclient_finish_command(c))
840                 return mpdclient_filelist_free(filelist);
842         filelist->updated = TRUE;
843         return filelist;
847 mpdclient_filelist_t *
848 mpdclient_filelist_search(mpdclient_t *c,
849                           int exact_match,
850                           int table,
851                           gchar *_filter)
853         mpdclient_filelist_t *filelist;
854         gchar *filter_utf8 = locale_to_utf8(_filter);
856         D("mpdclient_filelist_search(%s)\n", _filter);
857         filelist = mpdclient_filelist_search_utf8(c, exact_match, table,
858                                                   filter_utf8);
859         g_free(filter_utf8);
861         return filelist;
864 mpdclient_filelist_t *
865 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
867   if( filelist != NULL )
868     {    
869       gchar *path = g_strdup(filelist->path);
871       filelist = mpdclient_filelist_free(filelist);
872       filelist = mpdclient_filelist_get(c, path);
873       g_free(path);
874       return filelist;
875     }
876   return NULL;
879 filelist_entry_t *
880 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, struct mpd_song *song)
882   GList *list = g_list_first(fl->list);
884   while( list && song)
885     {
886       filelist_entry_t *entry = list->data;
887       mpd_InfoEntity *entity  = entry->entity;
889       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
890         {
891           struct mpd_song *song2 = entity->info.song;
893           if( strcmp(song->file, song2->file) == 0 )
894             {
895               return entry;
896             }
897         }
898       list = list->next;
899     }
900   return NULL;
903 int
904 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
906   GList *list = g_list_first(fl->list);
908   if( fl->list==NULL || fl->length<1 )
909     return 0;
911   mpd_sendCommandListBegin(c->connection);
912   while( list )
913     {
914       filelist_entry_t *entry = list->data;
915       mpd_InfoEntity *entity  = entry->entity;
917       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
918         {
919           struct mpd_song *song = entity->info.song;
921           mpd_sendAddCommand(c->connection, song->file);
922         }
923       list = list->next;
924     }
925   mpd_sendCommandListEnd(c->connection);
926   return mpdclient_finish_command(c);
936 GList *
937 mpdclient_get_artists_utf8(mpdclient_t *c)
939   gchar *str = NULL; 
940   GList *list = NULL;
942   D("mpdclient_get_artists()\n");
943   mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
944   while( (str=mpd_getNextArtist(c->connection)) )
945     {
946       list = g_list_append(list, (gpointer) str);
947     }
948   if( mpdclient_finish_command(c) )
949     {
950       return string_list_free(list);
951     }  
953   return list;
956 GList *
957 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
959   gchar *str = NULL; 
960   GList *list = NULL;
962   D("mpdclient_get_albums(%s)\n", artist_utf8);
963   mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
964   while( (str=mpd_getNextAlbum(c->connection)) )
965     {
966       list = g_list_append(list, (gpointer) str);
967     }
968   if( mpdclient_finish_command(c) )
969     {
970       return string_list_free(list);
971     }  
972   
973   return list;