Code

b13146f385baaf30191ecd39e0d382d4ee544d50
[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);
46 /* filelist sorting functions */
47 static gint
48 compare_filelistentry_dir(gconstpointer filelist_entry1, gconstpointer filelist_entry2)
49 {
50   mpd_InfoEntity *e1, *e2;
51   char *key1, *key2;
52   int n = 0;
54   e1 = ((filelist_entry_t *)filelist_entry1)->entity;
55   e2 = ((filelist_entry_t *)filelist_entry2)->entity;
56   if (e1 && e2 &&
57       e1->type == MPD_INFO_ENTITY_TYPE_DIRECTORY &&
58       e2->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
59     {
60       key1 = g_utf8_collate_key(e1->info.directory->path,-1);
61       key2 = g_utf8_collate_key(e2->info.directory->path,-1);
62       n = strcmp(key1,key2);
63       g_free(key1);
64       g_free(key2);
65     }
66   return n;
67 }
70 /* Error callbacks */
71 static gint
72 error_cb(mpdclient_t *c, gint error, gchar *msg)
73 {
74   GList *list = c->error_callbacks;
75   
76   if( list==NULL )
77     fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
79   while(list)
80     {
81       mpdc_error_cb_t cb = list->data;
82       if( cb )
83         cb(c, error, msg);
84       list=list->next;
85     }
86   mpd_clearError(c->connection);
87   return error;
88 }
90 #ifdef DEBUG
91 // Unused ath the moment
92 /*
93 #include "strfsong.h"
95 static gchar *
96 get_song_name(mpd_Song *song)
97 {
98   static gchar name[256];
100   strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
101   return name;
103 */
104 #endif
106 /****************************************************************************/
107 /*** mpdclient functions ****************************************************/
108 /****************************************************************************/
110 gint
111 mpdclient_finish_command(mpdclient_t *c) 
113   mpd_finishCommand(c->connection);
115   if( c->connection->error )
116     {
117       gchar *msg = locale_to_utf8(c->connection->errorStr);
118       gint error = c->connection->error;
119       if( error == MPD_ERROR_ACK )
120         error = error | (c->connection->errorCode << 8);
121       if(  c->connection->errorCode == MPD_ACK_ERROR_PERMISSION )
122         {
123           if(screen_auth(c) == 0) return 0;
124         }
125       error_cb(c, error, msg);
126       g_free(msg);
127       return error;
128     }
130   return 0;
133 mpdclient_t *
134 mpdclient_new(void)
136   mpdclient_t *c;
138   c = g_malloc0(sizeof(mpdclient_t));
140   return c;
143 mpdclient_t *
144 mpdclient_free(mpdclient_t *c)
146   mpdclient_disconnect(c);
147   g_list_free(c->error_callbacks);
148   g_list_free(c->playlist_callbacks);
149   g_list_free(c->browse_callbacks);
150   g_free(c);
152   return NULL;
155 gint
156 mpdclient_disconnect(mpdclient_t *c)
158   if( c->connection )
159     mpd_closeConnection(c->connection);
160   c->connection = NULL;
162   if( c->status )
163     mpd_freeStatus(c->status);
164   c->status = NULL;
166   if( c->playlist.list )
167     mpdclient_playlist_free(&c->playlist);
169   if( c->song )
170     c->song = NULL;
171   
172   return 0;
175 gint
176 mpdclient_connect(mpdclient_t *c, 
177                   gchar *host, 
178                   gint port, 
179                   gfloat timeout,
180                   gchar *password)
182   gint retval = 0;
183   
184   /* close any open connection */
185   if( c->connection )
186     mpdclient_disconnect(c);
188   /* connect to MPD */
189   c->connection = mpd_newConnection(host, port, timeout);
190   if( c->connection->error )
191     return error_cb(c, c->connection->error, c->connection->errorStr);
193   /* send password */
194   if( password )
195     {
196       mpd_sendPasswordCommand(c->connection, password);
197       retval = mpdclient_finish_command(c);
198     }
199   c->need_update = TRUE;
201   return retval;
204 gint
205 mpdclient_update(mpdclient_t *c)
207   gint retval = 0;
209   if( MPD_ERROR(c) )
210     return -1;
212   /* free the old status */
213   if( c->status )
214     mpd_freeStatus(c->status);
215   
216   /* retreive new status */
217   mpd_sendStatusCommand(c->connection);
218   c->status = mpd_getStatus(c->connection);
219   if( (retval=mpdclient_finish_command(c)) )
220     return retval;
221 #ifdef DEBUG
222   if( c->status->error )
223     D("status> %s\n", c->status->error);
224 #endif
226   /* check if the playlist needs an update */
227   if( c->playlist.id != c->status->playlist )
228     {
229       if( c->playlist.list )
230         retval = mpdclient_playlist_update_changes(c);
231       else
232         retval = mpdclient_playlist_update(c);
233     }
235   /* update the current song */
236   if( !c->song || c->status->songid != c->song->id )
237     {
238       c->song = playlist_get_song(c, c->status->song);
239     }
241   c->need_update = FALSE;
243   return retval;
247 /****************************************************************************/
248 /*** MPD Commands  **********************************************************/
249 /****************************************************************************/
251 gint 
252 mpdclient_cmd_play(mpdclient_t *c, gint index)
254 #ifdef ENABLE_SONG_ID
255   mpd_Song *song = playlist_get_song(c, index);
257   D("Play id:%d\n", song ? song->id : -1);
258   if( song )
259     mpd_sendPlayIdCommand(c->connection, song->id);
260   else
261     mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
262 #else
263   mpd_sendPlayCommand(c->connection, index);
264 #endif
265   c->need_update = TRUE;
266   return mpdclient_finish_command(c);
269 gint 
270 mpdclient_cmd_pause(mpdclient_t *c, gint value)
272   mpd_sendPauseCommand(c->connection, value);
273   return mpdclient_finish_command(c);
276 gint 
277 mpdclient_cmd_stop(mpdclient_t *c)
279   mpd_sendStopCommand(c->connection);
280   return mpdclient_finish_command(c);
283 gint 
284 mpdclient_cmd_next(mpdclient_t *c)
286   mpd_sendNextCommand(c->connection);
287   c->need_update = TRUE;
288   return mpdclient_finish_command(c);
291 gint 
292 mpdclient_cmd_prev(mpdclient_t *c)
294   mpd_sendPrevCommand(c->connection);
295   c->need_update = TRUE;
296   return mpdclient_finish_command(c);
299 gint 
300 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
302   D("Seek id:%d\n", id);
303   mpd_sendSeekIdCommand(c->connection, id, pos);
304   return mpdclient_finish_command(c);
307 gint 
308 mpdclient_cmd_shuffle(mpdclient_t *c)
310   mpd_sendShuffleCommand(c->connection);
311   c->need_update = TRUE;
312   return mpdclient_finish_command(c);
315 gint 
316 mpdclient_cmd_clear(mpdclient_t *c)
318   gint retval = 0;
320   mpd_sendClearCommand(c->connection);
321   retval = mpdclient_finish_command(c);
322   /* call playlist updated callback */
323   mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
324   c->need_update = TRUE;
325   return retval;
328 gint 
329 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
331   mpd_sendRepeatCommand(c->connection, value);
332   return mpdclient_finish_command(c);
335 gint 
336 mpdclient_cmd_random(mpdclient_t *c, gint value)
338   mpd_sendRandomCommand(c->connection, value);
339   return mpdclient_finish_command(c);
342 gint 
343 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
345   mpd_sendCrossfadeCommand(c->connection, value);
346   return mpdclient_finish_command(c);
349 gint 
350 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
352   mpd_sendUpdateCommand(c->connection, path ? path : "");
353   return mpdclient_finish_command(c);
356 gint 
357 mpdclient_cmd_volume(mpdclient_t *c, gint value)
359   mpd_sendSetvolCommand(c->connection, value);
360   return mpdclient_finish_command(c);
363 gint 
364 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
366   mpd_sendAddCommand(c->connection, path_utf8);
367   return mpdclient_finish_command(c);
370 gint 
371 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
373   gint retval;
374   gchar *path_utf8 = locale_to_utf8(path);
376   retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
377   g_free(path_utf8);
378   return retval;
381 gint 
382 mpdclient_cmd_add(mpdclient_t *c, mpd_Song *song)
383
384   gint retval = 0;
386   if( !song || !song->file )
387     return -1;
389   /* send the add command to mpd */
390   mpd_sendAddCommand(c->connection, song->file);
391   if( (retval=mpdclient_finish_command(c)) )
392     return retval;
394 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
395   /* add the song to playlist */
396   c->playlist.list = g_list_append(c->playlist.list, mpd_songDup(song));
397   c->playlist.length++;
399   /* increment the playlist id, so we dont retrives a new playlist */
400   c->playlist.id++;
402   /* call playlist updated callback */
403   mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
404 #else
405   c->need_update = TRUE;
406 #endif
408   return 0;
411 gint
412 mpdclient_cmd_delete(mpdclient_t *c, gint index)
414   gint retval = 0;
415   mpd_Song *song = playlist_get_song(c, index);
417   if( !song )
418     return -1;
420   /* send the delete command to mpd */
421 #ifdef ENABLE_SONG_ID
422   D("Delete id:%d\n", song->id);
423   mpd_sendDeleteIdCommand(c->connection, song->id);
424 #else
425   mpd_sendDeleteCommand(c->connection, index);
426 #endif
427   if( (retval=mpdclient_finish_command(c)) )
428     return retval;
430 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
431   /* increment the playlist id, so we dont retrive a new playlist */
432   c->playlist.id++;
434   /* remove the song from the playlist */
435   c->playlist.list = g_list_remove(c->playlist.list, (gpointer) song);
436   c->playlist.length = g_list_length(c->playlist.list);
438   /* call playlist updated callback */
439   mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
441   /* remove references to the song */
442   if( c->song == song )
443     {
444       c->song = NULL;   
445       c->need_update = TRUE;
446     }
448   /* free song */
449   mpd_freeSong(song);  
451 #else
452   c->need_update = TRUE;
453 #endif
455   return 0;
458 gint
459 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
461   gint n, index1, index2;
462   GList *item1, *item2;
463   gpointer data1, data2;
464   mpd_Song *song1, *song2;
466   if( old_index==new_index || new_index<0 || new_index>=c->playlist.length )
467     return -1;
469   song1 = playlist_get_song(c, old_index);
470   song2 = playlist_get_song(c, new_index);
472   /* send the move command to mpd */  
473 #ifdef ENABLE_SONG_ID
474   D("Swapping id:%d with id:%d\n", song1->id, song2->id);
475   mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
476 #else
477   D("Moving index %d to id:%d\n", old_index, new_index);
478   mpd_sendMoveCommand(c->connection, old_index, new_index);
479 #endif
480   if( (n=mpdclient_finish_command(c)) )
481     return n;
483 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
484   /* update the songs position field */
485   n = song1->pos;
486   song1->pos = song2->pos;
487   song2->pos = n;
488   index1 = MIN(old_index, new_index);
489   index2 = MAX(old_index, new_index);
490   /* retreive the list items */
491   item1 = g_list_nth(c->playlist.list, index1);
492   item2 = g_list_nth(c->playlist.list, index2);
493   /* retrieve the songs */
494   data1 = item1->data;
495   data2 = item2->data;
497   /* move the second item */
498   c->playlist.list = g_list_remove(c->playlist.list, data2);
499   c->playlist.list = g_list_insert_before(c->playlist.list, item1, data2);
501   /* move the first item */
502   if( index2-index1 >1 )
503     {
504       item2 = g_list_nth(c->playlist.list, index2);
505       c->playlist.list = g_list_remove(c->playlist.list, data1);
506       c->playlist.list = g_list_insert_before(c->playlist.list, item2, data1);
507     }
509   /* increment the playlist id, so we dont retrives a new playlist */
510   c->playlist.id++;
512 #else
513   c->need_update = TRUE;
514 #endif 
516   /* call playlist updated callback */
517   D("move> new_index=%d, old_index=%d\n", new_index, old_index);
518   mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
520   return 0;
523 gint 
524 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
526   gint retval = 0;
528   mpd_sendSaveCommand(c->connection, filename_utf8);
529   if( (retval=mpdclient_finish_command(c)) == 0 )
530     mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
531   return retval;
534 gint 
535 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
537   gint retval = 0;
538   gchar *filename_utf8 = locale_to_utf8(filename);
539   
540   retval = mpdclient_cmd_save_playlist_utf8(c, filename);
541   g_free(filename_utf8);
542   return retval;
545 gint 
546 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
548   mpd_sendLoadCommand(c->connection, filename_utf8);
549   c->need_update = TRUE;
550   return mpdclient_finish_command(c);
553 gint 
554 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
556   gint retval = 0;
558   mpd_sendRmCommand(c->connection, filename_utf8);
559   if( (retval=mpdclient_finish_command(c)) == 0 )
560     mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
561   return retval;
564 gint 
565 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
567   gint retval = 0;
568   gchar *filename_utf8 = locale_to_utf8(filename);
570   retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
571   g_free(filename_utf8);
572   return retval;
576 /****************************************************************************/
577 /*** Callback managment functions *******************************************/
578 /****************************************************************************/
579 static void
580 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
582   while(list)
583     {
584       mpdc_list_cb_t fn = list->data;
586       fn(c, event, data);
587       list=list->next;
588     }
591 void
592 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
594   do_list_callbacks(c, c->playlist_callbacks, event, data);
597 void
598 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
600   c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
603 void
604 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
606   c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
609 void
610 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
612   do_list_callbacks(c, c->browse_callbacks, event, data);
616 void
617 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
619   c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
622 void
623 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
625   c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
628 void
629 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
631   c->error_callbacks = g_list_append(c->error_callbacks, cb);
634 void
635 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
637   c->error_callbacks = g_list_remove(c->error_callbacks, cb);
640 /****************************************************************************/
641 /*** Playlist managment functions *******************************************/
642 /****************************************************************************/
644 gint
645 mpdclient_playlist_free(mpdclient_playlist_t *playlist)
647   GList *list = g_list_first(playlist->list);
649   while(list)
650     {
651       mpd_Song *song = (mpd_Song *) list->data;
652       mpd_freeSong(song);
653       list=list->next;
654     }
655   g_list_free(playlist->list);
656   memset(playlist, 0, sizeof(mpdclient_playlist_t));
657   return 0;
660 /* update playlist */
661 gint 
662 mpdclient_playlist_update(mpdclient_t *c)
664   mpd_InfoEntity *entity;
666   D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
668   if( MPD_ERROR(c) )
669     return -1;
671   if( c->playlist.list )
672     mpdclient_playlist_free(&c->playlist);
674   mpd_sendPlaylistInfoCommand(c->connection,-1);
675   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
676     {
677       if(entity->type==MPD_INFO_ENTITY_TYPE_SONG) 
678         {
679           mpd_Song *song = mpd_songDup(entity->info.song);
681           c->playlist.list = g_list_append(c->playlist.list, (gpointer) song);
682           c->playlist.length++;
683         }
684       mpd_freeInfoEntity(entity);
685     }
686   c->playlist.id = c->status->playlist;
687   c->song = NULL;
688   c->playlist.updated = TRUE;
690   /* call playlist updated callbacks */
691   mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
693   return mpdclient_finish_command(c);
696 #ifdef ENABLE_PLCHANGES
698 gint 
699 mpdclient_compare_songs(gconstpointer a, gconstpointer b)
701   mpd_Song *song1 = (mpd_Song *) a; 
702   mpd_Song *song2 = (mpd_Song *) b; 
704   return song1->pos - song2->pos;
709 /* update playlist (plchanges) */
710 gint 
711 mpdclient_playlist_update_changes(mpdclient_t *c)
713   mpd_InfoEntity *entity;
715   D("mpdclient_playlist_update_changes() [%lld -> %lld]\n", 
716     c->status->playlist, c->playlist.id);
718   if( MPD_ERROR(c) )
719     return -1;
721   mpd_sendPlChangesCommand(c->connection, c->playlist.id); 
723   while( (entity=mpd_getNextInfoEntity(c->connection)) != NULL   ) 
724     {
725       mpd_Song *song = entity->info.song;
727       if( song->pos < c->playlist.length )
728         {
729           GList *item = g_list_nth(c->playlist.list, song->pos);
731           /* update song */
732           D("updating pos:%d, id=%d [%p] - %s\n", 
733             song->pos, song->id, item, song->file);
734           mpd_freeSong((mpd_Song *) item->data);                     
735           item->data = mpd_songDup(song);
736         }
737       else
738         {
739           /* add a new song */
740           D("adding song at pos %d\n", song->pos);
741           c->playlist.list = g_list_append(c->playlist.list, 
742                                            (gpointer) mpd_songDup(song));
743         }
744       
745     }
747   /* remove trailing songs */
748   while( c->status->playlistLength < c->playlist.length )
749     {
750       GList *item = g_list_last(c->playlist.list);
752       /* Remove the last playlist entry */
753       D("removing song at pos %d\n", ((mpd_Song *) item->data)->pos);
754       mpd_freeSong((mpd_Song *) item->data);
755       c->playlist.list = g_list_delete_link(c->playlist.list, item);
756       c->playlist.length = g_list_length(c->playlist.list);   
757     }
759   c->song = NULL;
760   c->playlist.id = c->status->playlist;
761   c->playlist.updated = TRUE;
762   c->playlist.length = g_list_length(c->playlist.list);
764   mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
766   return 0;
769 #else
770 gint 
771 mpdclient_playlist_update_changes(mpdclient_t *c)
773   return mpdclient_playlist_update(c);
775 #endif
777 mpd_Song *
778 playlist_get_song(mpdclient_t *c, gint index)
780   return (mpd_Song *) g_list_nth_data(c->playlist.list, index);
783 GList *
784 playlist_lookup(mpdclient_t *c, int id)
786   GList *list = g_list_first(c->playlist.list);
788   while( list )
789     {
790       mpd_Song *song = (mpd_Song *) list->data;
791       if( song->id == id )
792         return list;
793       list=list->next;
794     }
795   return NULL;
798 mpd_Song *
799 playlist_lookup_song(mpdclient_t *c, gint id)
801   GList *list = c->playlist.list;
803   while( list )
804     {
805       mpd_Song *song = (mpd_Song *) list->data;
806       if( song->id == id )
807         return song;
808       list=list->next;
809     }
810   return NULL;
813 gint 
814 playlist_get_index(mpdclient_t *c, mpd_Song *song)
816   return g_list_index(c->playlist.list, song);
819 gint 
820 playlist_get_index_from_id(mpdclient_t *c, gint id)
822   return g_list_index(c->playlist.list, playlist_lookup_song(c, id));
825 gint
826 playlist_get_index_from_file(mpdclient_t *c, gchar *filename)
828   GList *list = c->playlist.list;
829   gint i=0;
831   while( list )
832     {
833       mpd_Song *song = (mpd_Song *) list->data;
834       if( strcmp(song->file, filename ) == 0 )  
835         return i;
836       list=list->next;
837       i++;
838     }
839   return -1;
843 /****************************************************************************/
844 /*** Filelist functions *****************************************************/
845 /****************************************************************************/
847 mpdclient_filelist_t *
848 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
850   GList *list = g_list_first(filelist->list);
852   D("mpdclient_filelist_free()\n");
853   if( list == NULL )
854     return NULL;
855   while( list!=NULL )
856     {
857       filelist_entry_t *entry = list->data;
859       if( entry->entity )
860         mpd_freeInfoEntity(entry->entity);
861       g_free(entry);
862       list=list->next;
863     }
864   g_list_free(filelist->list);
865   g_free(filelist->path);
866   filelist->path = NULL;
867   filelist->list = NULL;
868   filelist->length = 0;
869   g_free(filelist);
871   return NULL;
875 mpdclient_filelist_t *
876 mpdclient_filelist_get(mpdclient_t *c, gchar *path)
878   mpdclient_filelist_t *filelist;
879   mpd_InfoEntity *entity;
880   gchar *path_utf8 = locale_to_utf8(path);
881   gboolean has_dirs_only = TRUE;
883   D("mpdclient_filelist_get(%s)\n", path);
884   mpd_sendLsInfoCommand(c->connection, path_utf8);
885   filelist = g_malloc0(sizeof(mpdclient_filelist_t));
886   if( path && path[0] && strcmp(path, "/") )
887     {
888       /* add a dummy entry for ./.. */
889       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
890       entry->entity = NULL;
891       filelist->list = g_list_append(filelist->list, (gpointer) entry);
892       filelist->length++;
893     }
895   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
896     {
897       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
898       
899       entry->entity = entity;
900       filelist->list = g_list_append(filelist->list, (gpointer) entry);
901       filelist->length++;
903       if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY)
904         {
905           has_dirs_only = FALSE;
906         }
907     }
908   
909    /* If there's an error, ignore it.  We'll return an empty filelist. */
910    mpdclient_finish_command(c);
911   
912   g_free(path_utf8);
913   filelist->path = g_strdup(path);
914   filelist->updated = TRUE;
916   // If there are only directory entities in the filelist, we sort it
917   if (has_dirs_only)
918     {
919       D("mpdclient_filelist_get: only dirs; sorting!\n");
920       filelist->list = g_list_sort(filelist->list, compare_filelistentry_dir);
921     }
923   return filelist;
926 mpdclient_filelist_t *
927 mpdclient_filelist_search_utf8(mpdclient_t *c, 
928                                int exact_match,
929                                int table, 
930                                gchar *filter_utf8)
932   mpdclient_filelist_t *filelist;
933   mpd_InfoEntity *entity;
935   D("mpdclient_filelist_search(%s)\n", filter_utf8);
936   if( exact_match )
937     mpd_sendFindCommand(c->connection, table, filter_utf8);
938   else
939     mpd_sendSearchCommand(c->connection, table, filter_utf8);
940   filelist = g_malloc0(sizeof(mpdclient_filelist_t));
942   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
943     {
944       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
945       
946       entry->entity = entity;
947       filelist->list = g_list_append(filelist->list, (gpointer) entry);
948       filelist->length++;
949     }
950   
951   if( mpdclient_finish_command(c) )
952     return mpdclient_filelist_free(filelist);
954   filelist->updated = TRUE;
956   return filelist;
960 mpdclient_filelist_t *
961 mpdclient_filelist_search(mpdclient_t *c,
962                           int exact_match, 
963                           int table, 
964                           gchar *filter)
966   mpdclient_filelist_t *filelist;
967   gchar *filter_utf8 = locale_to_utf8(filter);
969   D("mpdclient_filelist_search(%s)\n", filter);
970   filelist = mpdclient_filelist_search_utf8(c,exact_match,table,filter_utf8);
971   g_free(filter_utf8);
973   return filelist;
976 mpdclient_filelist_t *
977 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
979   if( filelist != NULL )
980     {    
981       gchar *path = g_strdup(filelist->path);
983       filelist = mpdclient_filelist_free(filelist);
984       filelist = mpdclient_filelist_get(c, path);
985       g_free(path);
986       return filelist;
987     }
988   return NULL;
991 filelist_entry_t *
992 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song)
994   GList *list = g_list_first(fl->list);
996   while( list && song)
997     {
998       filelist_entry_t *entry = list->data;
999       mpd_InfoEntity *entity  = entry->entity;
1001       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
1002         {
1003           mpd_Song *song2 = entity->info.song;
1005           if( strcmp(song->file, song2->file) == 0 )
1006             {
1007               return entry;
1008             }
1009         }
1010       list = list->next;
1011     }
1012   return NULL;
1015 int
1016 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
1018   GList *list = g_list_first(fl->list);
1020   if( fl->list==NULL || fl->length<1 )
1021     return 0;
1023   mpd_sendCommandListBegin(c->connection);
1024   while( list )
1025     {
1026       filelist_entry_t *entry = list->data;
1027       mpd_InfoEntity *entity  = entry->entity;
1029       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
1030         {
1031           mpd_Song *song = entity->info.song;
1033           mpd_sendAddCommand(c->connection, song->file);
1034         }
1035       list = list->next;
1036     }
1037   mpd_sendCommandListEnd(c->connection);
1038   return mpdclient_finish_command(c);
1048 GList *
1049 mpdclient_get_artists_utf8(mpdclient_t *c)
1051   gchar *str = NULL; 
1052   GList *list = NULL;
1054   D("mpdclient_get_artists()\n");
1055   mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
1056   while( (str=mpd_getNextArtist(c->connection)) )
1057     {
1058       list = g_list_append(list, (gpointer) str);
1059     }
1060   if( mpdclient_finish_command(c) )
1061     {
1062       return string_list_free(list);
1063     }  
1065   return list;
1068 GList *
1069 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
1071   gchar *str = NULL; 
1072   GList *list = NULL;
1074   D("mpdclient_get_albums(%s)\n", artist_utf8);
1075   mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
1076   while( (str=mpd_getNextAlbum(c->connection)) )
1077     {
1078       list = g_list_append(list, (gpointer) str);
1079     }
1080   if( mpdclient_finish_command(c) )
1081     {
1082       return string_list_free(list);
1083     }  
1084   
1085   return list;