Code

mpdclient: abort ncmpc if out of memory
[ncmpc.git] / src / mpdclient.c
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2009 The Music Player Daemon Project
3  * Project homepage: http://musicpd.org
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
20 #include "mpdclient.h"
21 #include "filelist.h"
22 #include "screen_utils.h"
23 #include "config.h"
24 #include "options.h"
25 #include "strfsong.h"
26 #include "utils.h"
28 #include <mpd/client.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <string.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 static bool
44 MPD_ERROR(const struct mpdclient *client)
45 {
46         return client->connection == NULL ||
47                 mpd_connection_get_error(client->connection) != MPD_ERROR_SUCCESS;
48 }
50 /* filelist sorting functions */
51 static gint
52 compare_filelistentry(gconstpointer filelist_entry1,
53                           gconstpointer filelist_entry2)
54 {
55         const struct mpd_entity *e1, *e2;
56         int n = 0;
58         e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
59         e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
61         if (e1 != NULL && e2 != NULL &&
62             mpd_entity_get_type(e1) == mpd_entity_get_type(e2)) {
63                 switch (mpd_entity_get_type(e1)) {
64                 case MPD_ENTITY_TYPE_UNKNOWN:
65                         break;
66                 case MPD_ENTITY_TYPE_DIRECTORY:
67                         n = g_utf8_collate(mpd_directory_get_path(mpd_entity_get_directory(e1)),
68                                            mpd_directory_get_path(mpd_entity_get_directory(e2)));
69                         break;
70                 case MPD_ENTITY_TYPE_SONG:
71                         break;
72                 case MPD_ENTITY_TYPE_PLAYLIST:
73                         n = g_utf8_collate(mpd_playlist_get_path(mpd_entity_get_playlist(e1)),
74                                            mpd_playlist_get_path(mpd_entity_get_playlist(e2)));
75                 }
76         }
77         return n;
78 }
80 /* sort by list-format */
81 gint
82 compare_filelistentry_format(gconstpointer filelist_entry1,
83                              gconstpointer filelist_entry2)
84 {
85         const struct mpd_entity *e1, *e2;
86         char key1[BUFSIZE], key2[BUFSIZE];
87         int n = 0;
89         e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
90         e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
92         if (e1 && e2 &&
93             mpd_entity_get_type(e1) == MPD_ENTITY_TYPE_SONG &&
94             mpd_entity_get_type(e2) == MPD_ENTITY_TYPE_SONG) {
95                 strfsong(key1, BUFSIZE, options.list_format, mpd_entity_get_song(e1));
96                 strfsong(key2, BUFSIZE, options.list_format, mpd_entity_get_song(e2));
97                 n = strcmp(key1,key2);
98         }
100         return n;
104 /* Error callbacks */
105 static gint
106 error_cb(struct mpdclient *c, gint error, const gchar *msg)
108         GList *list = c->error_callbacks;
110         if (list == NULL)
111                 fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
113         while (list) {
114                 mpdc_error_cb_t cb = list->data;
115                 if (cb)
116                         cb(c, error, msg);
117                 list = list->next;
118         }
120         mpd_connection_clear_error(c->connection);
121         return error;
125 /****************************************************************************/
126 /*** mpdclient functions ****************************************************/
127 /****************************************************************************/
129 static gint
130 mpdclient_handle_error(struct mpdclient *c)
132         enum mpd_error error = mpd_connection_get_error(c->connection);
133         bool is_fatal = error != MPD_ERROR_SERVER;
135         assert(error != MPD_ERROR_SUCCESS);
137         if (error == MPD_ERROR_SERVER &&
138             mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION &&
139             screen_auth(c) == 0)
140                 return 0;
142         if (error == MPD_ERROR_SERVER)
143                 error = error | (mpd_connection_get_server_error(c->connection) << 8);
145         error_cb(c, error, mpd_connection_get_error_message(c->connection));
147         if (is_fatal)
148                 mpdclient_disconnect(c);
150         return error;
153 gint
154 mpdclient_finish_command(struct mpdclient *c)
156         return mpd_response_finish(c->connection)
157                 ? 0 : mpdclient_handle_error(c);
160 struct mpdclient *
161 mpdclient_new(void)
163         struct mpdclient *c;
165         c = g_new0(struct mpdclient, 1);
166         playlist_init(&c->playlist);
167         c->volume = MPD_STATUS_NO_VOLUME;
169         return c;
172 void
173 mpdclient_free(struct mpdclient *c)
175         mpdclient_disconnect(c);
177         mpdclient_playlist_free(&c->playlist);
179         g_list_free(c->error_callbacks);
180         g_list_free(c->playlist_callbacks);
181         g_list_free(c->browse_callbacks);
182         g_free(c);
185 gint
186 mpdclient_disconnect(struct mpdclient *c)
188         if (c->connection)
189                 mpd_connection_free(c->connection);
190         c->connection = NULL;
192         if (c->status)
193                 mpd_status_free(c->status);
194         c->status = NULL;
196         playlist_clear(&c->playlist);
198         if (c->song)
199                 c->song = NULL;
201         return 0;
204 gint
205 mpdclient_connect(struct mpdclient *c,
206                   const gchar *host,
207                   gint port,
208                   gfloat _timeout,
209                   const gchar *password)
211         gint retval = 0;
213         /* close any open connection */
214         if( c->connection )
215                 mpdclient_disconnect(c);
217         /* connect to MPD */
218         c->connection = mpd_connection_new(host, port, _timeout * 1000);
219         if (c->connection == NULL)
220                 g_error("Out of memory");
222         if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
223                 retval = error_cb(c, mpd_connection_get_error(c->connection),
224                                   mpd_connection_get_error_message(c->connection));
225                 if (retval != 0) {
226                         mpd_connection_free(c->connection);
227                         c->connection = NULL;
228                 }
230                 return retval;
231         }
233         /* send password */
234         if( password ) {
235                 mpd_send_password(c->connection, password);
236                 retval = mpdclient_finish_command(c);
237         }
238         c->need_update = TRUE;
240         return retval;
243 gint
244 mpdclient_update(struct mpdclient *c)
246         gint retval = 0;
248         c->volume = MPD_STATUS_NO_VOLUME;
250         if (MPD_ERROR(c))
251                 return -1;
253         /* free the old status */
254         if (c->status)
255                 mpd_status_free(c->status);
257         /* retrieve new status */
258         c->status = mpd_run_status(c->connection);
259         if (c->status == NULL)
260                 return mpdclient_handle_error(c);
262         if (c->updatingdb &&
263             c->updatingdb != mpd_status_get_update_id(c->status))
264                 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
266         c->updatingdb = mpd_status_get_update_id(c->status);
267         c->volume = mpd_status_get_volume(c->status);
269         /* check if the playlist needs an update */
270         if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
271                 if (playlist_is_empty(&c->playlist))
272                         retval = mpdclient_playlist_update_changes(c);
273                 else
274                         retval = mpdclient_playlist_update(c);
275         }
277         /* update the current song */
278         if (!c->song || mpd_status_get_song_id(c->status)) {
279                 c->song = playlist_get_song(c, mpd_status_get_song_pos(c->status));
280         }
282         c->need_update = FALSE;
284         return retval;
288 /****************************************************************************/
289 /*** MPD Commands  **********************************************************/
290 /****************************************************************************/
292 gint
293 mpdclient_cmd_play(struct mpdclient *c, gint idx)
295 #ifdef ENABLE_SONG_ID
296         struct mpd_song *song = playlist_get_song(c, idx);
298         if (MPD_ERROR(c))
299                 return -1;
301         if (song)
302                 mpd_send_play_id(c->connection, mpd_song_get_id(song));
303         else
304                 mpd_send_play(c->connection);
305 #else
306         if (MPD_ERROR(c))
307                 return -1;
309         mpd_sendPlayCommand(c->connection, idx);
310 #endif
311         c->need_update = TRUE;
312         return mpdclient_finish_command(c);
315 gint
316 mpdclient_cmd_pause(struct mpdclient *c, gint value)
318         if (MPD_ERROR(c))
319                 return -1;
321         mpd_send_pause(c->connection, value);
322         return mpdclient_finish_command(c);
325 gint
326 mpdclient_cmd_crop(struct mpdclient *c)
328         struct mpd_status *status;
329         bool playing;
330         int length, current;
332         if (MPD_ERROR(c))
333                 return -1;
335         status = mpd_run_status(c->connection);
336         if (status == NULL)
337                 return mpdclient_handle_error(c);
339         playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
340                 mpd_status_get_state(status) == MPD_STATE_PAUSE;
341         length = mpd_status_get_queue_length(status);
342         current = mpd_status_get_song_pos(status);
344         mpd_status_free(status);
346         if (!playing || length < 2)
347                 return 0;
349         mpd_command_list_begin(c->connection, false);
351         while (--length >= 0)
352                 if (length != current)
353                         mpd_send_delete(c->connection, length);
355         mpd_command_list_end(c->connection);
357         return mpdclient_finish_command(c);
360 gint
361 mpdclient_cmd_stop(struct mpdclient *c)
363         if (MPD_ERROR(c))
364                 return -1;
366         mpd_send_stop(c->connection);
367         return mpdclient_finish_command(c);
370 gint
371 mpdclient_cmd_next(struct mpdclient *c)
373         if (MPD_ERROR(c))
374                 return -1;
376         mpd_send_next(c->connection);
377         c->need_update = TRUE;
378         return mpdclient_finish_command(c);
381 gint
382 mpdclient_cmd_prev(struct mpdclient *c)
384         if (MPD_ERROR(c))
385                 return -1;
387         mpd_send_previous(c->connection);
388         c->need_update = TRUE;
389         return mpdclient_finish_command(c);
392 gint
393 mpdclient_cmd_seek(struct mpdclient *c, gint id, gint pos)
395         if (MPD_ERROR(c))
396                 return -1;
398         mpd_send_seek_id(c->connection, id, pos);
399         return mpdclient_finish_command(c);
402 gint
403 mpdclient_cmd_shuffle(struct mpdclient *c)
405         if (MPD_ERROR(c))
406                 return -1;
408         mpd_send_shuffle(c->connection);
409         c->need_update = TRUE;
410         return mpdclient_finish_command(c);
413 gint
414 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
416         mpd_send_shuffle_range(c->connection, start, end);
417         c->need_update = TRUE;
418         return mpdclient_finish_command(c);
421 gint
422 mpdclient_cmd_clear(struct mpdclient *c)
424         gint retval = 0;
426         if (MPD_ERROR(c))
427                 return -1;
429         mpd_send_clear(c->connection);
430         retval = mpdclient_finish_command(c);
431         /* call playlist updated callback */
432         mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
433         c->need_update = TRUE;
434         return retval;
437 gint
438 mpdclient_cmd_repeat(struct mpdclient *c, gint value)
440         if (MPD_ERROR(c))
441                 return -1;
443         mpd_send_repeat(c->connection, value);
444         return mpdclient_finish_command(c);
447 gint
448 mpdclient_cmd_random(struct mpdclient *c, gint value)
450         if (MPD_ERROR(c))
451                 return -1;
453         mpd_send_random(c->connection, value);
454         return mpdclient_finish_command(c);
457 gint
458 mpdclient_cmd_single(struct mpdclient *c, gint value)
460         if (MPD_ERROR(c))
461                 return -1;
463         mpd_send_single(c->connection, value);
464         return mpdclient_finish_command(c);
467 gint
468 mpdclient_cmd_consume(struct mpdclient *c, gint value)
470         if (MPD_ERROR(c))
471                 return -1;
473         mpd_send_consume(c->connection, value);
474         return mpdclient_finish_command(c);
477 gint
478 mpdclient_cmd_crossfade(struct mpdclient *c, gint value)
480         if (MPD_ERROR(c))
481                 return -1;
483         mpd_send_crossfade(c->connection, value);
484         return mpdclient_finish_command(c);
487 gint
488 mpdclient_cmd_db_update(struct mpdclient *c, const gchar *path)
490         gint ret;
492         if (MPD_ERROR(c))
493                 return -1;
495         mpd_send_update(c->connection, path ? path : "");
496         ret = mpdclient_finish_command(c);
498         if (ret == 0)
499                 /* set updatingDb to make sure the browse callback
500                    gets called even if the update has finished before
501                    status is updated */
502                 c->updatingdb = 1;
504         return ret;
507 gint
508 mpdclient_cmd_volume(struct mpdclient *c, gint value)
510         if (MPD_ERROR(c))
511                 return -1;
513         mpd_send_set_volume(c->connection, value);
514         return mpdclient_finish_command(c);
517 gint mpdclient_cmd_volume_up(struct mpdclient *c)
519         if (MPD_ERROR(c))
520                 return -1;
522         if (c->status == NULL ||
523             mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
524                 return 0;
526         if (c->volume == MPD_STATUS_NO_VOLUME)
527                 c->volume = mpd_status_get_volume(c->status);
529         if (c->volume >= 100)
530                 return 0;
532         return mpdclient_cmd_volume(c, ++c->volume);
535 gint mpdclient_cmd_volume_down(struct mpdclient *c)
537         if (MPD_ERROR(c))
538                 return -1;
540         if (c->status == NULL ||
541             mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
542                 return 0;
544         if (c->volume == MPD_STATUS_NO_VOLUME)
545                 c->volume = mpd_status_get_volume(c->status);
547         if (c->volume <= 0)
548                 return 0;
550         return mpdclient_cmd_volume(c, --c->volume);
553 gint
554 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
556         if (MPD_ERROR(c))
557                 return -1;
559         mpd_send_add(c->connection, path_utf8);
560         return mpdclient_finish_command(c);
563 gint
564 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
566         gint retval = 0;
568         if (MPD_ERROR(c))
569                 return -1;
571         if (song == NULL)
572                 return -1;
574         /* send the add command to mpd */
575         mpd_send_add(c->connection, mpd_song_get_uri(song));
576         if( (retval=mpdclient_finish_command(c)) )
577                 return retval;
579 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
580         /* add the song to playlist */
581         playlist_append(&c->playlist, song);
583         /* increment the playlist id, so we don't retrieve a new playlist */
584         c->playlist.id++;
586         /* call playlist updated callback */
587         mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
588 #else
589         c->need_update = TRUE;
590 #endif
592         return 0;
595 gint
596 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
598         gint retval = 0;
599         struct mpd_song *song;
601         if (MPD_ERROR(c))
602                 return -1;
604         if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
605                 return -1;
607         song = playlist_get(&c->playlist, idx);
609         /* send the delete command to mpd */
610 #ifdef ENABLE_SONG_ID
611         mpd_send_delete_id(c->connection, mpd_song_get_id(song));
612 #else
613         mpd_send_delete(c->connection, idx);
614 #endif
615         if( (retval=mpdclient_finish_command(c)) )
616                 return retval;
618 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
619         /* increment the playlist id, so we don't retrieve a new playlist */
620         c->playlist.id++;
622         /* remove the song from the playlist */
623         playlist_remove_reuse(&c->playlist, idx);
625         /* call playlist updated callback */
626         mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
628         /* remove references to the song */
629         if (c->song == song) {
630                 c->song = NULL;
631                 c->need_update = TRUE;
632         }
634         mpd_song_free(song);
636 #else
637         c->need_update = TRUE;
638 #endif
640         return 0;
643 gint
644 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
646         gint n;
647         struct mpd_song *song1, *song2;
649         if (MPD_ERROR(c))
650                 return -1;
652         if (old_index == new_index || new_index < 0 ||
653             (guint)new_index >= c->playlist.list->len)
654                 return -1;
656         song1 = playlist_get(&c->playlist, old_index);
657         song2 = playlist_get(&c->playlist, new_index);
659         /* send the move command to mpd */
660 #ifdef ENABLE_SONG_ID
661         mpd_send_swap_id(c->connection,
662                          mpd_song_get_id(song1), mpd_song_get_id(song2));
663 #else
664         mpd_send_move(c->connection, old_index, new_index);
665 #endif
666         if( (n=mpdclient_finish_command(c)) )
667                 return n;
669 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
670         /* update the playlist */
671         playlist_swap(&c->playlist, old_index, new_index);
673         /* increment the playlist id, so we don't retrieve a new playlist */
674         c->playlist.id++;
676 #else
677         c->need_update = TRUE;
678 #endif
680         /* call playlist updated callback */
681         mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
683         return 0;
686 gint
687 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
689         gint retval = 0;
691         if (MPD_ERROR(c))
692                 return -1;
694         mpd_send_save(c->connection, filename_utf8);
695         if ((retval = mpdclient_finish_command(c)) == 0)
696                 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
697         return retval;
700 gint
701 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
703         if (MPD_ERROR(c))
704                 return -1;
706         mpd_send_load(c->connection, filename_utf8);
707         c->need_update = TRUE;
708         return mpdclient_finish_command(c);
711 gint
712 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
714         gint retval = 0;
716         if (MPD_ERROR(c))
717                 return -1;
719         mpd_send_rm(c->connection, filename_utf8);
720         if ((retval = mpdclient_finish_command(c)) == 0)
721                 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
722         return retval;
726 /****************************************************************************/
727 /*** Callback management functions ******************************************/
728 /****************************************************************************/
730 static void
731 do_list_callbacks(struct mpdclient *c, GList *list, gint event, gpointer data)
733         while (list) {
734                 mpdc_list_cb_t fn = list->data;
736                 fn(c, event, data);
737                 list = list->next;
738         }
741 void
742 mpdclient_playlist_callback(struct mpdclient *c, int event, gpointer data)
744         do_list_callbacks(c, c->playlist_callbacks, event, data);
747 void
748 mpdclient_install_playlist_callback(struct mpdclient *c,mpdc_list_cb_t cb)
750         c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
753 void
754 mpdclient_remove_playlist_callback(struct mpdclient *c, mpdc_list_cb_t cb)
756         c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
759 void
760 mpdclient_browse_callback(struct mpdclient *c, int event, gpointer data)
762         do_list_callbacks(c, c->browse_callbacks, event, data);
766 void
767 mpdclient_install_browse_callback(struct mpdclient *c,mpdc_list_cb_t cb)
769         c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
772 void
773 mpdclient_remove_browse_callback(struct mpdclient *c, mpdc_list_cb_t cb)
775         c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
778 void
779 mpdclient_install_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
781         c->error_callbacks = g_list_append(c->error_callbacks, cb);
784 void
785 mpdclient_remove_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
787         c->error_callbacks = g_list_remove(c->error_callbacks, cb);
791 /****************************************************************************/
792 /*** Playlist management functions ******************************************/
793 /****************************************************************************/
795 /* update playlist */
796 gint
797 mpdclient_playlist_update(struct mpdclient *c)
799         struct mpd_entity *entity;
801         if (MPD_ERROR(c))
802                 return -1;
804         playlist_clear(&c->playlist);
806         mpd_send_list_queue_meta(c->connection);
807         while ((entity = mpd_recv_entity(c->connection))) {
808                 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
809                         playlist_append(&c->playlist, mpd_entity_get_song(entity));
811                 mpd_entity_free(entity);
812         }
814         c->playlist.id = mpd_status_get_queue_version(c->status);
815         c->song = NULL;
817         /* call playlist updated callbacks */
818         mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
820         return mpdclient_finish_command(c);
823 #ifdef ENABLE_PLCHANGES
825 /* update playlist (plchanges) */
826 gint
827 mpdclient_playlist_update_changes(struct mpdclient *c)
829         struct mpd_song *song;
830         guint length;
832         if (MPD_ERROR(c))
833                 return -1;
835         mpd_send_queue_changes_meta(c->connection, c->playlist.id);
837         while ((song = mpd_recv_song(c->connection)) != NULL) {
838                 int pos = mpd_song_get_pos(song);
840                 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
841                         /* update song */
842                         playlist_replace(&c->playlist, pos, song);
843                 } else {
844                         /* add a new song */
845                         playlist_append(&c->playlist, song);
846                 }
848                 mpd_song_free(song);
849         }
851         /* remove trailing songs */
853         length = mpd_status_get_queue_length(c->status);
854         while (length < c->playlist.list->len) {
855                 guint pos = c->playlist.list->len - 1;
857                 /* Remove the last playlist entry */
858                 playlist_remove(&c->playlist, pos);
859         }
861         c->song = NULL;
862         c->playlist.id = mpd_status_get_queue_version(c->status);
864         mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
866         return 0;
869 #else
870 gint
871 mpdclient_playlist_update_changes(struct mpdclient *c)
873         return mpdclient_playlist_update(c);
875 #endif
878 /****************************************************************************/
879 /*** Filelist functions *****************************************************/
880 /****************************************************************************/
882 struct filelist *
883 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
885         struct filelist *filelist;
886         struct mpd_entity *entity;
888         if (MPD_ERROR(c))
889                 return NULL;
891         mpd_send_list_meta(c->connection, path);
892         filelist = filelist_new();
893         if (path && path[0] && strcmp(path, "/"))
894                 /* add a dummy entry for ./.. */
895                 filelist_append(filelist, NULL);
897         while ((entity = mpd_recv_entity(c->connection)) != NULL)
898                 filelist_append(filelist, entity);
900         /* If there's an error, ignore it.  We'll return an empty filelist. */
901         mpdclient_finish_command(c);
903         filelist_sort_dir_play(filelist, compare_filelistentry);
905         return filelist;
908 static struct filelist *
909 mpdclient_recv_filelist_response(struct mpdclient *c)
911         struct filelist *filelist;
912         struct mpd_entity *entity;
914         filelist = filelist_new();
916         while ((entity = mpd_recv_entity(c->connection)) != NULL)
917                 filelist_append(filelist, entity);
919         if (mpdclient_finish_command(c)) {
920                 filelist_free(filelist);
921                 return NULL;
922         }
924         return filelist;
927 struct filelist *
928 mpdclient_filelist_search(struct mpdclient *c,
929                           int exact_match,
930                           enum mpd_tag_type tag,
931                           gchar *filter_utf8)
933         if (MPD_ERROR(c))
934                 return NULL;
936         mpd_search_db_songs(c->connection, exact_match);
937         mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
938                                       tag, filter_utf8);
939         mpd_search_commit(c->connection);
941         return mpdclient_recv_filelist_response(c);
944 int
945 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
947         guint i;
949         if (MPD_ERROR(c))
950                 return -1;
952         if (filelist_is_empty(fl))
953                 return 0;
955         mpd_command_list_begin(c->connection, false);
957         for (i = 0; i < filelist_length(fl); ++i) {
958                 struct filelist_entry *entry = filelist_get(fl, i);
959                 struct mpd_entity *entity  = entry->entity;
961                 if (entity != NULL &&
962                     mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
963                         const struct mpd_song *song =
964                                 mpd_entity_get_song(entity);
965                         const char *uri = mpd_song_get_uri(song);
967                         if (uri != NULL)
968                                 mpd_send_add(c->connection, uri);
969                 }
970         }
972         mpd_command_list_end(c->connection);
973         return mpdclient_finish_command(c);
976 GList *
977 mpdclient_get_artists(struct mpdclient *c)
979         GList *list = NULL;
980         struct mpd_pair *pair;
982         if (MPD_ERROR(c))
983                return NULL;
985         mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
986         mpd_search_commit(c->connection);
988         while ((pair = mpd_recv_pair_tag(c->connection,
989                                          MPD_TAG_ARTIST)) != NULL) {
990                 list = g_list_append(list, g_strdup(pair->value));
991                 mpd_return_pair(c->connection, pair);
992         }
994         if (mpdclient_finish_command(c))
995                 return string_list_free(list);
997         return list;
1000 GList *
1001 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
1003         GList *list = NULL;
1004         struct mpd_pair *pair;
1006         if (MPD_ERROR(c))
1007                return NULL;
1009         mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
1010         if (artist_utf8 != NULL)
1011                 mpd_search_add_tag_constraint(c->connection,
1012                                               MPD_OPERATOR_DEFAULT,
1013                                               MPD_TAG_ARTIST, artist_utf8);
1014         mpd_search_commit(c->connection);
1016         while ((pair = mpd_recv_pair_tag(c->connection,
1017                                          MPD_TAG_ALBUM)) != NULL) {
1018                 list = g_list_append(list, g_strdup(pair->value));
1019                 mpd_return_pair(c->connection, pair);
1020         }
1022         if (mpdclient_finish_command(c))
1023                 return string_list_free(list);
1025         return list;