Code

way too much stuff to describe here
[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"
33 #include "strfsong.h"
35 #undef  ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
36 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
37 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
38 #define ENABLE_SONG_ID
39 #define ENABLE_PLCHANGES 
41 #define BUFSIZE 1024
43 #define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)
45 /* from utils.c */
46 extern GList *string_list_free(GList *string_list);
49 /* filelist sorting functions */
50 static gint
51 compare_filelistentry_dir(gconstpointer filelist_entry1, gconstpointer filelist_entry2)
52 {
53   mpd_InfoEntity *e1, *e2;
54   char *key1, *key2;
55   int n = 0;
57   e1 = ((filelist_entry_t *)filelist_entry1)->entity;
58   e2 = ((filelist_entry_t *)filelist_entry2)->entity;
59   if (e1 && e2 &&
60       e1->type == MPD_INFO_ENTITY_TYPE_DIRECTORY &&
61       e2->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
62     {
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     }
69   return n;
70 }
72 /* sort by list-format */
73 gint
74 compare_filelistentry_format(gconstpointer filelist_entry1, gconstpointer filelist_entry2)
75 {
76   mpd_InfoEntity *e1, *e2;
77   char key1[BUFSIZE], key2[BUFSIZE];
78   int n = 0;
80   e1 = ((filelist_entry_t *)filelist_entry1)->entity;
81   e2 = ((filelist_entry_t *)filelist_entry2)->entity;
82   if (e1 && e2 &&
83       e1->type == MPD_INFO_ENTITY_TYPE_SONG &&
84       e2->type == MPD_INFO_ENTITY_TYPE_SONG)
85     {
86       strfsong(key1, BUFSIZE, LIST_FORMAT, e1->info.song);
87       strfsong(key2, BUFSIZE, LIST_FORMAT, e2->info.song);
88       n = strcmp(key1,key2);
89     }
90   return n;
91 }
94 /* Error callbacks */
95 static gint
96 error_cb(mpdclient_t *c, gint error, gchar *msg)
97 {
98   GList *list = c->error_callbacks;
99   
100   if( list==NULL )
101     fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
103   while(list)
104     {
105       mpdc_error_cb_t cb = list->data;
106       if( cb )
107         cb(c, error, msg);
108       list=list->next;
109     }
110   mpd_clearError(c->connection);
111   return error;
114 #ifdef DEBUG
115 // Unused ath the moment
116 /*
117 #include "strfsong.h"
119 static gchar *
120 get_song_name(mpd_Song *song)
122   static gchar name[256];
124   strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
125   return name;
127 */
128 #endif
130 /****************************************************************************/
131 /*** mpdclient functions ****************************************************/
132 /****************************************************************************/
134 gint
135 mpdclient_finish_command(mpdclient_t *c) 
137   mpd_finishCommand(c->connection);
139   if( c->connection->error )
140     {
141       gchar *msg = locale_to_utf8(c->connection->errorStr);
142       gint error = c->connection->error;
143       if( error == MPD_ERROR_ACK )
144         error = error | (c->connection->errorCode << 8);
145       if(  c->connection->errorCode == MPD_ACK_ERROR_PERMISSION )
146         {
147           if(screen_auth(c) == 0) return 0;
148         }
149       error_cb(c, error, msg);
150       g_free(msg);
151       return error;
152     }
154   return 0;
157 mpdclient_t *
158 mpdclient_new(void)
160   mpdclient_t *c;
162   c = g_malloc0(sizeof(mpdclient_t));
164   return c;
167 mpdclient_t *
168 mpdclient_free(mpdclient_t *c)
170   mpdclient_disconnect(c);
171   g_list_free(c->error_callbacks);
172   g_list_free(c->playlist_callbacks);
173   g_list_free(c->browse_callbacks);
174   g_free(c);
176   return NULL;
179 gint
180 mpdclient_disconnect(mpdclient_t *c)
182   if( c->connection )
183     mpd_closeConnection(c->connection);
184   c->connection = NULL;
186   if( c->status )
187     mpd_freeStatus(c->status);
188   c->status = NULL;
190   if( c->playlist.list )
191     mpdclient_playlist_free(&c->playlist);
193   if( c->song )
194     c->song = NULL;
195   
196   return 0;
199 gint
200 mpdclient_connect(mpdclient_t *c, 
201                   gchar *host, 
202                   gint port, 
203                   gfloat timeout,
204                   gchar *password)
206   gint retval = 0;
207   
208   /* close any open connection */
209   if( c->connection )
210     mpdclient_disconnect(c);
212   /* connect to MPD */
213   c->connection = mpd_newConnection(host, port, timeout);
214   if( c->connection->error )
215     return error_cb(c, c->connection->error, c->connection->errorStr);
217   /* send password */
218   if( password )
219     {
220       mpd_sendPasswordCommand(c->connection, password);
221       retval = mpdclient_finish_command(c);
222     }
223   c->need_update = TRUE;
225   return retval;
228 gint
229 mpdclient_update(mpdclient_t *c)
231   gint retval = 0;
233   if( MPD_ERROR(c) )
234     return -1;
236   /* free the old status */
237   if( c->status )
238     mpd_freeStatus(c->status);
239   
240   /* retreive new status */
241   mpd_sendStatusCommand(c->connection);
242   c->status = mpd_getStatus(c->connection);
243   if( (retval=mpdclient_finish_command(c)) )
244     return retval;
245 #ifdef DEBUG
246   if( c->status->error )
247     D("status> %s\n", c->status->error);
248 #endif
250   /* check if the playlist needs an update */
251   if( c->playlist.id != c->status->playlist )
252     {
253       if( c->playlist.list )
254         retval = mpdclient_playlist_update_changes(c);
255       else
256         retval = mpdclient_playlist_update(c);
257     }
259   /* update the current song */
260   if( !c->song || c->status->songid != c->song->id )
261     {
262       c->song = playlist_get_song(c, c->status->song);
263     }
265   c->need_update = FALSE;
267   return retval;
271 /****************************************************************************/
272 /*** MPD Commands  **********************************************************/
273 /****************************************************************************/
275 gint 
276 mpdclient_cmd_play(mpdclient_t *c, gint index)
278 #ifdef ENABLE_SONG_ID
279   mpd_Song *song = playlist_get_song(c, index);
281   D("Play id:%d\n", song ? song->id : -1);
282   if( song )
283     mpd_sendPlayIdCommand(c->connection, song->id);
284   else
285     mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
286 #else
287   mpd_sendPlayCommand(c->connection, index);
288 #endif
289   c->need_update = TRUE;
290   return mpdclient_finish_command(c);
293 gint 
294 mpdclient_cmd_pause(mpdclient_t *c, gint value)
296   mpd_sendPauseCommand(c->connection, value);
297   return mpdclient_finish_command(c);
300 gint 
301 mpdclient_cmd_stop(mpdclient_t *c)
303   mpd_sendStopCommand(c->connection);
304   return mpdclient_finish_command(c);
307 gint 
308 mpdclient_cmd_next(mpdclient_t *c)
310   mpd_sendNextCommand(c->connection);
311   c->need_update = TRUE;
312   return mpdclient_finish_command(c);
315 gint 
316 mpdclient_cmd_prev(mpdclient_t *c)
318   mpd_sendPrevCommand(c->connection);
319   c->need_update = TRUE;
320   return mpdclient_finish_command(c);
323 gint 
324 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
326   D("Seek id:%d\n", id);
327   mpd_sendSeekIdCommand(c->connection, id, pos);
328   return mpdclient_finish_command(c);
331 gint 
332 mpdclient_cmd_shuffle(mpdclient_t *c)
334   mpd_sendShuffleCommand(c->connection);
335   c->need_update = TRUE;
336   return mpdclient_finish_command(c);
339 gint 
340 mpdclient_cmd_clear(mpdclient_t *c)
342   gint retval = 0;
344   mpd_sendClearCommand(c->connection);
345   retval = mpdclient_finish_command(c);
346   /* call playlist updated callback */
347   mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
348   c->need_update = TRUE;
349   return retval;
352 gint 
353 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
355   mpd_sendRepeatCommand(c->connection, value);
356   return mpdclient_finish_command(c);
359 gint 
360 mpdclient_cmd_random(mpdclient_t *c, gint value)
362   mpd_sendRandomCommand(c->connection, value);
363   return mpdclient_finish_command(c);
366 gint 
367 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
369   mpd_sendCrossfadeCommand(c->connection, value);
370   return mpdclient_finish_command(c);
373 gint 
374 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
376   mpd_sendUpdateCommand(c->connection, path ? path : "");
377   return mpdclient_finish_command(c);
380 gint 
381 mpdclient_cmd_volume(mpdclient_t *c, gint value)
383   mpd_sendSetvolCommand(c->connection, value);
384   return mpdclient_finish_command(c);
387 gint 
388 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
390   mpd_sendAddCommand(c->connection, path_utf8);
391   return mpdclient_finish_command(c);
394 gint 
395 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
397   gint retval;
398   gchar *path_utf8 = locale_to_utf8(path);
400   retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
401   g_free(path_utf8);
402   return retval;
405 gint 
406 mpdclient_cmd_add(mpdclient_t *c, mpd_Song *song)
407
408   gint retval = 0;
410   if( !song || !song->file )
411     return -1;
413   /* send the add command to mpd */
414   mpd_sendAddCommand(c->connection, song->file);
415   if( (retval=mpdclient_finish_command(c)) )
416     return retval;
418 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
419   /* add the song to playlist */
420   c->playlist.list = g_list_append(c->playlist.list, mpd_songDup(song));
421   c->playlist.length++;
423   /* increment the playlist id, so we dont retrives a new playlist */
424   c->playlist.id++;
426   /* call playlist updated callback */
427   mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
428 #else
429   c->need_update = TRUE;
430 #endif
432   return 0;
435 gint
436 mpdclient_cmd_delete(mpdclient_t *c, gint index)
438   gint retval = 0;
439   mpd_Song *song = playlist_get_song(c, index);
441   if( !song )
442     return -1;
444   /* send the delete command to mpd */
445 #ifdef ENABLE_SONG_ID
446   D("Delete id:%d\n", song->id);
447   mpd_sendDeleteIdCommand(c->connection, song->id);
448 #else
449   mpd_sendDeleteCommand(c->connection, index);
450 #endif
451   if( (retval=mpdclient_finish_command(c)) )
452     return retval;
454 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
455   /* increment the playlist id, so we dont retrive a new playlist */
456   c->playlist.id++;
458   /* remove the song from the playlist */
459   c->playlist.list = g_list_remove(c->playlist.list, (gpointer) song);
460   c->playlist.length = g_list_length(c->playlist.list);
462   /* call playlist updated callback */
463   mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
465   /* remove references to the song */
466   if( c->song == song )
467     {
468       c->song = NULL;   
469       c->need_update = TRUE;
470     }
472   /* free song */
473   mpd_freeSong(song);  
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, index1, index2;
486   GList *item1, *item2;
487   gpointer data1, data2;
488   mpd_Song *song1, *song2;
490   if( old_index==new_index || new_index<0 || new_index>=c->playlist.length )
491     return -1;
493   song1 = playlist_get_song(c, old_index);
494   song2 = playlist_get_song(c, new_index);
496   /* send the move command to mpd */  
497 #ifdef ENABLE_SONG_ID
498   D("Swapping id:%d with id:%d\n", song1->id, song2->id);
499   mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
500 #else
501   D("Moving index %d to id:%d\n", old_index, new_index);
502   mpd_sendMoveCommand(c->connection, old_index, new_index);
503 #endif
504   if( (n=mpdclient_finish_command(c)) )
505     return n;
507 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
508   /* update the songs position field */
509   n = song1->pos;
510   song1->pos = song2->pos;
511   song2->pos = n;
512   index1 = MIN(old_index, new_index);
513   index2 = MAX(old_index, new_index);
514   /* retreive the list items */
515   item1 = g_list_nth(c->playlist.list, index1);
516   item2 = g_list_nth(c->playlist.list, index2);
517   /* retrieve the songs */
518   data1 = item1->data;
519   data2 = item2->data;
521   /* move the second item */
522   c->playlist.list = g_list_remove(c->playlist.list, data2);
523   c->playlist.list = g_list_insert_before(c->playlist.list, item1, data2);
525   /* move the first item */
526   if( index2-index1 >1 )
527     {
528       item2 = g_list_nth(c->playlist.list, index2);
529       c->playlist.list = g_list_remove(c->playlist.list, data1);
530       c->playlist.list = g_list_insert_before(c->playlist.list, item2, data1);
531     }
533   /* increment the playlist id, so we dont retrives a new playlist */
534   c->playlist.id++;
536 #else
537   c->need_update = TRUE;
538 #endif 
540   /* call playlist updated callback */
541   D("move> new_index=%d, old_index=%d\n", new_index, old_index);
542   mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
544   return 0;
547 gint 
548 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
550   gint retval = 0;
552   mpd_sendSaveCommand(c->connection, filename_utf8);
553   if( (retval=mpdclient_finish_command(c)) == 0 )
554     mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
555   return retval;
558 gint 
559 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
561   gint retval = 0;
562   gchar *filename_utf8 = locale_to_utf8(filename);
563   
564   retval = mpdclient_cmd_save_playlist_utf8(c, filename);
565   g_free(filename_utf8);
566   return retval;
569 gint 
570 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
572   mpd_sendLoadCommand(c->connection, filename_utf8);
573   c->need_update = TRUE;
574   return mpdclient_finish_command(c);
577 gint 
578 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
580   gint retval = 0;
582   mpd_sendRmCommand(c->connection, filename_utf8);
583   if( (retval=mpdclient_finish_command(c)) == 0 )
584     mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
585   return retval;
588 gint 
589 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
591   gint retval = 0;
592   gchar *filename_utf8 = locale_to_utf8(filename);
594   retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
595   g_free(filename_utf8);
596   return retval;
600 /****************************************************************************/
601 /*** Callback managment functions *******************************************/
602 /****************************************************************************/
603 static void
604 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
606   while(list)
607     {
608       mpdc_list_cb_t fn = list->data;
610       fn(c, event, data);
611       list=list->next;
612     }
615 void
616 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
618   do_list_callbacks(c, c->playlist_callbacks, event, data);
621 void
622 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
624   c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
627 void
628 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
630   c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
633 void
634 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
636   do_list_callbacks(c, c->browse_callbacks, event, data);
640 void
641 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
643   c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
646 void
647 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
649   c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
652 void
653 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
655   c->error_callbacks = g_list_append(c->error_callbacks, cb);
658 void
659 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
661   c->error_callbacks = g_list_remove(c->error_callbacks, cb);
664 /****************************************************************************/
665 /*** Playlist managment functions *******************************************/
666 /****************************************************************************/
668 gint
669 mpdclient_playlist_free(mpdclient_playlist_t *playlist)
671   GList *list = g_list_first(playlist->list);
673   while(list)
674     {
675       mpd_Song *song = (mpd_Song *) list->data;
676       mpd_freeSong(song);
677       list=list->next;
678     }
679   g_list_free(playlist->list);
680   memset(playlist, 0, sizeof(mpdclient_playlist_t));
681   return 0;
684 /* update playlist */
685 gint 
686 mpdclient_playlist_update(mpdclient_t *c)
688   mpd_InfoEntity *entity;
690   D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
692   if( MPD_ERROR(c) )
693     return -1;
695   if( c->playlist.list )
696     mpdclient_playlist_free(&c->playlist);
698   mpd_sendPlaylistInfoCommand(c->connection,-1);
699   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
700     {
701       if(entity->type==MPD_INFO_ENTITY_TYPE_SONG) 
702         {
703           mpd_Song *song = mpd_songDup(entity->info.song);
705           c->playlist.list = g_list_append(c->playlist.list, (gpointer) song);
706           c->playlist.length++;
707         }
708       mpd_freeInfoEntity(entity);
709     }
710   c->playlist.id = c->status->playlist;
711   c->song = NULL;
712   c->playlist.updated = TRUE;
714   /* call playlist updated callbacks */
715   mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
717   return mpdclient_finish_command(c);
720 #ifdef ENABLE_PLCHANGES
722 gint 
723 mpdclient_compare_songs(gconstpointer a, gconstpointer b)
725   mpd_Song *song1 = (mpd_Song *) a; 
726   mpd_Song *song2 = (mpd_Song *) b; 
728   return song1->pos - song2->pos;
733 /* update playlist (plchanges) */
734 gint 
735 mpdclient_playlist_update_changes(mpdclient_t *c)
737   mpd_InfoEntity *entity;
739   D("mpdclient_playlist_update_changes() [%lld -> %lld]\n", 
740     c->status->playlist, c->playlist.id);
742   if( MPD_ERROR(c) )
743     return -1;
745   mpd_sendPlChangesCommand(c->connection, c->playlist.id); 
747   while( (entity=mpd_getNextInfoEntity(c->connection)) != NULL   ) 
748     {
749       mpd_Song *song = entity->info.song;
751       if( song->pos < c->playlist.length )
752         {
753           GList *item = g_list_nth(c->playlist.list, song->pos);
755           /* update song */
756           D("updating pos:%d, id=%d [%p] - %s\n", 
757             song->pos, song->id, item, song->file);
758           mpd_freeSong((mpd_Song *) item->data);                     
759           item->data = mpd_songDup(song);
760         }
761       else
762         {
763           /* add a new song */
764           D("adding song at pos %d\n", song->pos);
765           c->playlist.list = g_list_append(c->playlist.list, 
766                                            (gpointer) mpd_songDup(song));
767         }
768       
769     }
771   /* remove trailing songs */
772   while( c->status->playlistLength < c->playlist.length )
773     {
774       GList *item = g_list_last(c->playlist.list);
776       /* Remove the last playlist entry */
777       D("removing song at pos %d\n", ((mpd_Song *) item->data)->pos);
778       mpd_freeSong((mpd_Song *) item->data);
779       c->playlist.list = g_list_delete_link(c->playlist.list, item);
780       c->playlist.length = g_list_length(c->playlist.list);   
781     }
783   c->song = NULL;
784   c->playlist.id = c->status->playlist;
785   c->playlist.updated = TRUE;
786   c->playlist.length = g_list_length(c->playlist.list);
788   mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
790   return 0;
793 #else
794 gint 
795 mpdclient_playlist_update_changes(mpdclient_t *c)
797   return mpdclient_playlist_update(c);
799 #endif
801 mpd_Song *
802 playlist_get_song(mpdclient_t *c, gint index)
804   return (mpd_Song *) g_list_nth_data(c->playlist.list, index);
807 GList *
808 playlist_lookup(mpdclient_t *c, int id)
810   GList *list = g_list_first(c->playlist.list);
812   while( list )
813     {
814       mpd_Song *song = (mpd_Song *) list->data;
815       if( song->id == id )
816         return list;
817       list=list->next;
818     }
819   return NULL;
822 mpd_Song *
823 playlist_lookup_song(mpdclient_t *c, gint id)
825   GList *list = c->playlist.list;
827   while( list )
828     {
829       mpd_Song *song = (mpd_Song *) list->data;
830       if( song->id == id )
831         return song;
832       list=list->next;
833     }
834   return NULL;
837 gint 
838 playlist_get_index(mpdclient_t *c, mpd_Song *song)
840   return g_list_index(c->playlist.list, song);
843 gint 
844 playlist_get_index_from_id(mpdclient_t *c, gint id)
846   return g_list_index(c->playlist.list, playlist_lookup_song(c, id));
849 gint
850 playlist_get_index_from_file(mpdclient_t *c, gchar *filename)
852   GList *list = c->playlist.list;
853   gint i=0;
855   while( list )
856     {
857       mpd_Song *song = (mpd_Song *) list->data;
858       if( strcmp(song->file, filename ) == 0 )  
859         return i;
860       list=list->next;
861       i++;
862     }
863   return -1;
867 /****************************************************************************/
868 /*** Filelist functions *****************************************************/
869 /****************************************************************************/
871 mpdclient_filelist_t *
872 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
874   GList *list = g_list_first(filelist->list);
876   D("mpdclient_filelist_free()\n");
877   if( list == NULL )
878     return NULL;
879   while( list!=NULL )
880     {
881       filelist_entry_t *entry = list->data;
883       if( entry->entity )
884         mpd_freeInfoEntity(entry->entity);
885       g_free(entry);
886       list=list->next;
887     }
888   g_list_free(filelist->list);
889   g_free(filelist->path);
890   filelist->path = NULL;
891   filelist->list = NULL;
892   filelist->length = 0;
893   g_free(filelist);
895   return NULL;
899 mpdclient_filelist_t *
900 mpdclient_filelist_get(mpdclient_t *c, gchar *path)
902   mpdclient_filelist_t *filelist;
903   mpd_InfoEntity *entity;
904   gchar *path_utf8 = locale_to_utf8(path);
905   gboolean has_dirs_only = TRUE;
907   D("mpdclient_filelist_get(%s)\n", path);
908   mpd_sendLsInfoCommand(c->connection, path_utf8);
909   filelist = g_malloc0(sizeof(mpdclient_filelist_t));
910   if( path && path[0] && strcmp(path, "/") )
911     {
912       /* add a dummy entry for ./.. */
913       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
914       entry->entity = NULL;
915       filelist->list = g_list_append(filelist->list, (gpointer) entry);
916       filelist->length++;
917     }
919   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
920     {
921       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
922       
923       entry->entity = entity;
924       filelist->list = g_list_append(filelist->list, (gpointer) entry);
925       filelist->length++;
927       if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY)
928         {
929           has_dirs_only = FALSE;
930         }
931     }
932   
933    /* If there's an error, ignore it.  We'll return an empty filelist. */
934    mpdclient_finish_command(c);
935   
936   g_free(path_utf8);
937   filelist->path = g_strdup(path);
938   filelist->updated = TRUE;
940   // If there are only directory entities in the filelist, we sort it
941   if (has_dirs_only)
942     {
943       D("mpdclient_filelist_get: only dirs; sorting!\n");
944       filelist->list = g_list_sort(filelist->list, compare_filelistentry_dir);
945     }
947   return filelist;
950 mpdclient_filelist_t *
951 mpdclient_filelist_search_utf8(mpdclient_t *c, 
952                                int exact_match,
953                                int table, 
954                                gchar *filter_utf8)
956   mpdclient_filelist_t *filelist;
957   mpd_InfoEntity *entity;
959   D("mpdclient_filelist_search(%s)\n", filter_utf8);
960   if( exact_match )
961     mpd_sendFindCommand(c->connection, table, filter_utf8);
962   else
963     mpd_sendSearchCommand(c->connection, table, filter_utf8);
964   filelist = g_malloc0(sizeof(mpdclient_filelist_t));
966   while( (entity=mpd_getNextInfoEntity(c->connection)) ) 
967     {
968       filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
969       
970       entry->entity = entity;
971       filelist->list = g_list_append(filelist->list, (gpointer) entry);
972       filelist->length++;
973     }
974   
975   if( mpdclient_finish_command(c) )
976     return mpdclient_filelist_free(filelist);
978   filelist->updated = TRUE;
980   return filelist;
984 mpdclient_filelist_t *
985 mpdclient_filelist_search(mpdclient_t *c,
986                           int exact_match, 
987                           int table, 
988                           gchar *filter)
990   mpdclient_filelist_t *filelist;
991   gchar *filter_utf8 = locale_to_utf8(filter);
993   D("mpdclient_filelist_search(%s)\n", filter);
994   filelist = mpdclient_filelist_search_utf8(c,exact_match,table,filter_utf8);
995   g_free(filter_utf8);
997   return filelist;
1000 mpdclient_filelist_t *
1001 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
1003   if( filelist != NULL )
1004     {    
1005       gchar *path = g_strdup(filelist->path);
1007       filelist = mpdclient_filelist_free(filelist);
1008       filelist = mpdclient_filelist_get(c, path);
1009       g_free(path);
1010       return filelist;
1011     }
1012   return NULL;
1015 filelist_entry_t *
1016 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song)
1018   GList *list = g_list_first(fl->list);
1020   while( list && song)
1021     {
1022       filelist_entry_t *entry = list->data;
1023       mpd_InfoEntity *entity  = entry->entity;
1025       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
1026         {
1027           mpd_Song *song2 = entity->info.song;
1029           if( strcmp(song->file, song2->file) == 0 )
1030             {
1031               return entry;
1032             }
1033         }
1034       list = list->next;
1035     }
1036   return NULL;
1039 int
1040 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
1042   GList *list = g_list_first(fl->list);
1044   if( fl->list==NULL || fl->length<1 )
1045     return 0;
1047   mpd_sendCommandListBegin(c->connection);
1048   while( list )
1049     {
1050       filelist_entry_t *entry = list->data;
1051       mpd_InfoEntity *entity  = entry->entity;
1053       if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
1054         {
1055           mpd_Song *song = entity->info.song;
1057           mpd_sendAddCommand(c->connection, song->file);
1058         }
1059       list = list->next;
1060     }
1061   mpd_sendCommandListEnd(c->connection);
1062   return mpdclient_finish_command(c);
1072 GList *
1073 mpdclient_get_artists_utf8(mpdclient_t *c)
1075   gchar *str = NULL; 
1076   GList *list = NULL;
1078   D("mpdclient_get_artists()\n");
1079   mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
1080   while( (str=mpd_getNextArtist(c->connection)) )
1081     {
1082       list = g_list_append(list, (gpointer) str);
1083     }
1084   if( mpdclient_finish_command(c) )
1085     {
1086       return string_list_free(list);
1087     }  
1089   return list;
1092 GList *
1093 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
1095   gchar *str = NULL; 
1096   GList *list = NULL;
1098   D("mpdclient_get_albums(%s)\n", artist_utf8);
1099   mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
1100   while( (str=mpd_getNextAlbum(c->connection)) )
1101     {
1102       list = g_list_append(list, (gpointer) str);
1103     }
1104   if( mpdclient_finish_command(c) )
1105     {
1106       return string_list_free(list);
1107     }  
1108   
1109   return list;