Code

e7024d298d1dea24c2f20fd6f177e4a220140f88
[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_client.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
39 #define BUFSIZE 1024
41 static bool
42 MPD_ERROR(const struct mpdclient *client)
43 {
44         return client->connection == NULL ||
45                 mpd_connection_get_error(client->connection) != MPD_ERROR_SUCCESS;
46 }
48 /* filelist sorting functions */
49 static gint
50 compare_filelistentry(gconstpointer filelist_entry1,
51                           gconstpointer filelist_entry2)
52 {
53         const struct mpd_entity *e1, *e2;
54         int n = 0;
56         e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
57         e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
59         if (e1 != NULL && e2 != NULL &&
60             mpd_entity_get_type(e1) == mpd_entity_get_type(e2)) {
61                 switch (mpd_entity_get_type(e1)) {
62                 case MPD_ENTITY_TYPE_UNKNOWN:
63                         break;
64                 case MPD_ENTITY_TYPE_DIRECTORY:
65                         n = g_utf8_collate(mpd_directory_get_path(mpd_entity_get_directory(e1)),
66                                            mpd_directory_get_path(mpd_entity_get_directory(e2)));
67                         break;
68                 case MPD_ENTITY_TYPE_SONG:
69                         break;
70                 case MPD_ENTITY_TYPE_PLAYLIST:
71                         n = g_utf8_collate(mpd_playlist_get_path(mpd_entity_get_playlist(e1)),
72                                            mpd_playlist_get_path(mpd_entity_get_playlist(e2)));
73                 }
74         }
75         return n;
76 }
78 /* sort by list-format */
79 gint
80 compare_filelistentry_format(gconstpointer filelist_entry1,
81                              gconstpointer filelist_entry2)
82 {
83         const struct mpd_entity *e1, *e2;
84         char key1[BUFSIZE], key2[BUFSIZE];
85         int n = 0;
87         e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
88         e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
90         if (e1 && e2 &&
91             mpd_entity_get_type(e1) == MPD_ENTITY_TYPE_SONG &&
92             mpd_entity_get_type(e2) == MPD_ENTITY_TYPE_SONG) {
93                 strfsong(key1, BUFSIZE, options.list_format, mpd_entity_get_song(e1));
94                 strfsong(key2, BUFSIZE, options.list_format, mpd_entity_get_song(e2));
95                 n = strcmp(key1,key2);
96         }
98         return n;
99 }
102 /****************************************************************************/
103 /*** mpdclient functions ****************************************************/
104 /****************************************************************************/
106 gint
107 mpdclient_handle_error(struct mpdclient *c)
109         enum mpd_error error = mpd_connection_get_error(c->connection);
111         assert(error != MPD_ERROR_SUCCESS);
113         if (error == MPD_ERROR_SERVER &&
114             mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION &&
115             screen_auth(c))
116                 return 0;
118         if (error == MPD_ERROR_SERVER)
119                 error = error | (mpd_connection_get_server_error(c->connection) << 8);
121         for (GList *list = c->error_callbacks; list != NULL;
122              list = list->next) {
123                 mpdc_error_cb_t cb = list->data;
124                 cb(c, error, mpd_connection_get_error_message(c->connection));
125         }
127         if (!mpd_connection_clear_error(c->connection))
128                 mpdclient_disconnect(c);
130         return error;
133 gint
134 mpdclient_finish_command(struct mpdclient *c)
136         return mpd_response_finish(c->connection)
137                 ? 0 : mpdclient_handle_error(c);
140 struct mpdclient *
141 mpdclient_new(void)
143         struct mpdclient *c;
145         c = g_new0(struct mpdclient, 1);
146         playlist_init(&c->playlist);
147         c->volume = MPD_STATUS_NO_VOLUME;
149         return c;
152 void
153 mpdclient_free(struct mpdclient *c)
155         mpdclient_disconnect(c);
157         mpdclient_playlist_free(&c->playlist);
159         g_list_free(c->error_callbacks);
160         g_list_free(c->playlist_callbacks);
161         g_list_free(c->browse_callbacks);
162         g_free(c);
165 gint
166 mpdclient_disconnect(struct mpdclient *c)
168         if (c->connection)
169                 mpd_connection_free(c->connection);
170         c->connection = NULL;
172         if (c->status)
173                 mpd_status_free(c->status);
174         c->status = NULL;
176         playlist_clear(&c->playlist);
178         if (c->song)
179                 c->song = NULL;
181         return 0;
184 gint
185 mpdclient_connect(struct mpdclient *c,
186                   const gchar *host,
187                   gint port,
188                   gfloat _timeout,
189                   const gchar *password)
191         gint retval = 0;
193         /* close any open connection */
194         if( c->connection )
195                 mpdclient_disconnect(c);
197         /* connect to MPD */
198         c->connection = mpd_connection_new(host, port, _timeout * 1000);
199         if (c->connection == NULL)
200                 g_error("Out of memory");
202         if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
203                 retval = mpdclient_handle_error(c);
204                 if (retval != 0) {
205                         mpd_connection_free(c->connection);
206                         c->connection = NULL;
207                 }
209                 return retval;
210         }
212         /* send password */
213         if( password ) {
214                 mpd_send_password(c->connection, password);
215                 retval = mpdclient_finish_command(c);
216         }
218         return retval;
221 gint
222 mpdclient_update(struct mpdclient *c)
224         gint retval = 0;
226         c->volume = MPD_STATUS_NO_VOLUME;
228         if (MPD_ERROR(c))
229                 return -1;
231         /* free the old status */
232         if (c->status)
233                 mpd_status_free(c->status);
235         /* retrieve new status */
236         c->status = mpd_run_status(c->connection);
237         if (c->status == NULL)
238                 return mpdclient_handle_error(c);
240         if (c->updatingdb &&
241             c->updatingdb != mpd_status_get_update_id(c->status))
242                 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
244         c->updatingdb = mpd_status_get_update_id(c->status);
245         c->volume = mpd_status_get_volume(c->status);
247         /* check if the playlist needs an update */
248         if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
249                 if (playlist_is_empty(&c->playlist))
250                         retval = mpdclient_playlist_update_changes(c);
251                 else
252                         retval = mpdclient_playlist_update(c);
253         }
255         /* update the current song */
256         if (!c->song || mpd_status_get_song_id(c->status)) {
257                 c->song = playlist_get_song(c, mpd_status_get_song_pos(c->status));
258         }
260         return retval;
264 /****************************************************************************/
265 /*** MPD Commands  **********************************************************/
266 /****************************************************************************/
268 gint
269 mpdclient_cmd_play(struct mpdclient *c, gint idx)
271         struct mpd_song *song = playlist_get_song(c, idx);
273         if (MPD_ERROR(c))
274                 return -1;
276         if (song)
277                 mpd_send_play_id(c->connection, mpd_song_get_id(song));
278         else
279                 mpd_send_play(c->connection);
281         return mpdclient_finish_command(c);
284 gint
285 mpdclient_cmd_pause(struct mpdclient *c, gint value)
287         if (MPD_ERROR(c))
288                 return -1;
290         mpd_send_pause(c->connection, value);
291         return mpdclient_finish_command(c);
294 gint
295 mpdclient_cmd_crop(struct mpdclient *c)
297         struct mpd_status *status;
298         bool playing;
299         int length, current;
301         if (MPD_ERROR(c))
302                 return -1;
304         status = mpd_run_status(c->connection);
305         if (status == NULL)
306                 return mpdclient_handle_error(c);
308         playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
309                 mpd_status_get_state(status) == MPD_STATE_PAUSE;
310         length = mpd_status_get_queue_length(status);
311         current = mpd_status_get_song_pos(status);
313         mpd_status_free(status);
315         if (!playing || length < 2)
316                 return 0;
318         mpd_command_list_begin(c->connection, false);
320         while (--length >= 0)
321                 if (length != current)
322                         mpd_send_delete(c->connection, length);
324         mpd_command_list_end(c->connection);
326         return mpdclient_finish_command(c);
329 gint
330 mpdclient_cmd_stop(struct mpdclient *c)
332         if (MPD_ERROR(c))
333                 return -1;
335         mpd_send_stop(c->connection);
336         return mpdclient_finish_command(c);
339 gint
340 mpdclient_cmd_next(struct mpdclient *c)
342         if (MPD_ERROR(c))
343                 return -1;
345         mpd_send_next(c->connection);
346         return mpdclient_finish_command(c);
349 gint
350 mpdclient_cmd_prev(struct mpdclient *c)
352         if (MPD_ERROR(c))
353                 return -1;
355         mpd_send_previous(c->connection);
356         return mpdclient_finish_command(c);
359 gint
360 mpdclient_cmd_seek(struct mpdclient *c, gint id, gint pos)
362         if (MPD_ERROR(c))
363                 return -1;
365         mpd_send_seek_id(c->connection, id, pos);
366         return mpdclient_finish_command(c);
369 gint
370 mpdclient_cmd_shuffle(struct mpdclient *c)
372         if (MPD_ERROR(c))
373                 return -1;
375         mpd_send_shuffle(c->connection);
376         return mpdclient_finish_command(c);
379 gint
380 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
382         mpd_send_shuffle_range(c->connection, start, end);
383         return mpdclient_finish_command(c);
386 gint
387 mpdclient_cmd_clear(struct mpdclient *c)
389         gint retval = 0;
391         if (MPD_ERROR(c))
392                 return -1;
394         mpd_send_clear(c->connection);
395         retval = mpdclient_finish_command(c);
396         /* call playlist updated callback */
397         mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
398         return retval;
401 gint
402 mpdclient_cmd_repeat(struct mpdclient *c, gint value)
404         if (MPD_ERROR(c))
405                 return -1;
407         mpd_send_repeat(c->connection, value);
408         return mpdclient_finish_command(c);
411 gint
412 mpdclient_cmd_random(struct mpdclient *c, gint value)
414         if (MPD_ERROR(c))
415                 return -1;
417         mpd_send_random(c->connection, value);
418         return mpdclient_finish_command(c);
421 gint
422 mpdclient_cmd_single(struct mpdclient *c, gint value)
424         if (MPD_ERROR(c))
425                 return -1;
427         mpd_send_single(c->connection, value);
428         return mpdclient_finish_command(c);
431 gint
432 mpdclient_cmd_consume(struct mpdclient *c, gint value)
434         if (MPD_ERROR(c))
435                 return -1;
437         mpd_send_consume(c->connection, value);
438         return mpdclient_finish_command(c);
441 gint
442 mpdclient_cmd_crossfade(struct mpdclient *c, gint value)
444         if (MPD_ERROR(c))
445                 return -1;
447         mpd_send_crossfade(c->connection, value);
448         return mpdclient_finish_command(c);
451 gint
452 mpdclient_cmd_db_update(struct mpdclient *c, const gchar *path)
454         gint ret;
456         if (MPD_ERROR(c))
457                 return -1;
459         mpd_send_update(c->connection, path ? path : "");
460         ret = mpdclient_finish_command(c);
462         if (ret == 0)
463                 /* set updatingDb to make sure the browse callback
464                    gets called even if the update has finished before
465                    status is updated */
466                 c->updatingdb = 1;
468         return ret;
471 gint
472 mpdclient_cmd_volume(struct mpdclient *c, gint value)
474         if (MPD_ERROR(c))
475                 return -1;
477         mpd_send_set_volume(c->connection, value);
478         return mpdclient_finish_command(c);
481 gint mpdclient_cmd_volume_up(struct mpdclient *c)
483         if (MPD_ERROR(c))
484                 return -1;
486         if (c->status == NULL ||
487             mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
488                 return 0;
490         if (c->volume == MPD_STATUS_NO_VOLUME)
491                 c->volume = mpd_status_get_volume(c->status);
493         if (c->volume >= 100)
494                 return 0;
496         return mpdclient_cmd_volume(c, ++c->volume);
499 gint mpdclient_cmd_volume_down(struct mpdclient *c)
501         if (MPD_ERROR(c))
502                 return -1;
504         if (c->status == NULL ||
505             mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
506                 return 0;
508         if (c->volume == MPD_STATUS_NO_VOLUME)
509                 c->volume = mpd_status_get_volume(c->status);
511         if (c->volume <= 0)
512                 return 0;
514         return mpdclient_cmd_volume(c, --c->volume);
517 gint
518 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
520         if (MPD_ERROR(c))
521                 return -1;
523         mpd_send_add(c->connection, path_utf8);
524         return mpdclient_finish_command(c);
527 gint
528 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
530         gint retval = 0;
532         if (MPD_ERROR(c))
533                 return -1;
535         if (song == NULL)
536                 return -1;
538         /* send the add command to mpd */
539         mpd_send_add(c->connection, mpd_song_get_uri(song));
540         if( (retval=mpdclient_finish_command(c)) )
541                 return retval;
543 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
544         /* add the song to playlist */
545         playlist_append(&c->playlist, song);
547         /* increment the playlist id, so we don't retrieve a new playlist */
548         c->playlist.id++;
550         /* call playlist updated callback */
551         mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
552 #endif
554         return 0;
557 gint
558 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
560         gint retval = 0;
561         struct mpd_song *song;
563         if (MPD_ERROR(c))
564                 return -1;
566         if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
567                 return -1;
569         song = playlist_get(&c->playlist, idx);
571         /* send the delete command to mpd */
572         mpd_send_delete_id(c->connection, mpd_song_get_id(song));
573         if( (retval=mpdclient_finish_command(c)) )
574                 return retval;
576 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
577         /* increment the playlist id, so we don't retrieve a new playlist */
578         c->playlist.id++;
580         /* remove the song from the playlist */
581         playlist_remove_reuse(&c->playlist, idx);
583         /* call playlist updated callback */
584         mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
586         /* remove references to the song */
587         if (c->song == song)
588                 c->song = NULL;
590         mpd_song_free(song);
591 #endif
593         return 0;
596 gint
597 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
599         gint n;
600         struct mpd_song *song1, *song2;
602         if (MPD_ERROR(c))
603                 return -1;
605         if (old_index == new_index || new_index < 0 ||
606             (guint)new_index >= c->playlist.list->len)
607                 return -1;
609         song1 = playlist_get(&c->playlist, old_index);
610         song2 = playlist_get(&c->playlist, new_index);
612         /* send the move command to mpd */
613         mpd_send_swap_id(c->connection,
614                          mpd_song_get_id(song1), mpd_song_get_id(song2));
615         if( (n=mpdclient_finish_command(c)) )
616                 return n;
618 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
619         /* update the playlist */
620         playlist_swap(&c->playlist, old_index, new_index);
622         /* increment the playlist id, so we don't retrieve a new playlist */
623         c->playlist.id++;
624 #endif
626         /* call playlist updated callback */
627         mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
629         return 0;
632 gint
633 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
635         gint retval = 0;
637         if (MPD_ERROR(c))
638                 return -1;
640         mpd_send_save(c->connection, filename_utf8);
641         if ((retval = mpdclient_finish_command(c)) == 0)
642                 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
643         return retval;
646 gint
647 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
649         if (MPD_ERROR(c))
650                 return -1;
652         mpd_send_load(c->connection, filename_utf8);
653         return mpdclient_finish_command(c);
656 gint
657 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
659         gint retval = 0;
661         if (MPD_ERROR(c))
662                 return -1;
664         mpd_send_rm(c->connection, filename_utf8);
665         if ((retval = mpdclient_finish_command(c)) == 0)
666                 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
667         return retval;
671 /****************************************************************************/
672 /*** Callback management functions ******************************************/
673 /****************************************************************************/
675 static void
676 do_list_callbacks(struct mpdclient *c, GList *list, gint event, gpointer data)
678         while (list) {
679                 mpdc_list_cb_t fn = list->data;
681                 fn(c, event, data);
682                 list = list->next;
683         }
686 void
687 mpdclient_playlist_callback(struct mpdclient *c, int event, gpointer data)
689         do_list_callbacks(c, c->playlist_callbacks, event, data);
692 void
693 mpdclient_install_playlist_callback(struct mpdclient *c,mpdc_list_cb_t cb)
695         c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
698 void
699 mpdclient_remove_playlist_callback(struct mpdclient *c, mpdc_list_cb_t cb)
701         c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
704 void
705 mpdclient_browse_callback(struct mpdclient *c, int event, gpointer data)
707         do_list_callbacks(c, c->browse_callbacks, event, data);
711 void
712 mpdclient_install_browse_callback(struct mpdclient *c,mpdc_list_cb_t cb)
714         c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
717 void
718 mpdclient_remove_browse_callback(struct mpdclient *c, mpdc_list_cb_t cb)
720         c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
723 void
724 mpdclient_install_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
726         c->error_callbacks = g_list_append(c->error_callbacks, cb);
729 void
730 mpdclient_remove_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
732         c->error_callbacks = g_list_remove(c->error_callbacks, cb);
736 /****************************************************************************/
737 /*** Playlist management functions ******************************************/
738 /****************************************************************************/
740 /* update playlist */
741 gint
742 mpdclient_playlist_update(struct mpdclient *c)
744         struct mpd_entity *entity;
746         if (MPD_ERROR(c))
747                 return -1;
749         playlist_clear(&c->playlist);
751         mpd_send_list_queue_meta(c->connection);
752         while ((entity = mpd_recv_entity(c->connection))) {
753                 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
754                         playlist_append(&c->playlist, mpd_entity_get_song(entity));
756                 mpd_entity_free(entity);
757         }
759         c->playlist.id = mpd_status_get_queue_version(c->status);
760         c->song = NULL;
762         /* call playlist updated callbacks */
763         mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
765         return mpdclient_finish_command(c);
768 /* update playlist (plchanges) */
769 gint
770 mpdclient_playlist_update_changes(struct mpdclient *c)
772         struct mpd_song *song;
773         guint length;
775         if (MPD_ERROR(c))
776                 return -1;
778         mpd_send_queue_changes_meta(c->connection, c->playlist.id);
780         while ((song = mpd_recv_song(c->connection)) != NULL) {
781                 int pos = mpd_song_get_pos(song);
783                 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
784                         /* update song */
785                         playlist_replace(&c->playlist, pos, song);
786                 } else {
787                         /* add a new song */
788                         playlist_append(&c->playlist, song);
789                 }
791                 mpd_song_free(song);
792         }
794         /* remove trailing songs */
796         length = mpd_status_get_queue_length(c->status);
797         while (length < c->playlist.list->len) {
798                 guint pos = c->playlist.list->len - 1;
800                 /* Remove the last playlist entry */
801                 playlist_remove(&c->playlist, pos);
802         }
804         c->song = NULL;
805         c->playlist.id = mpd_status_get_queue_version(c->status);
807         mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
809         return 0;
813 /****************************************************************************/
814 /*** Filelist functions *****************************************************/
815 /****************************************************************************/
817 struct filelist *
818 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
820         struct filelist *filelist;
821         struct mpd_entity *entity;
823         if (MPD_ERROR(c))
824                 return NULL;
826         mpd_send_list_meta(c->connection, path);
827         filelist = filelist_new();
828         if (path && path[0] && strcmp(path, "/"))
829                 /* add a dummy entry for ./.. */
830                 filelist_append(filelist, NULL);
832         while ((entity = mpd_recv_entity(c->connection)) != NULL)
833                 filelist_append(filelist, entity);
835         /* If there's an error, ignore it.  We'll return an empty filelist. */
836         mpdclient_finish_command(c);
838         filelist_sort_dir_play(filelist, compare_filelistentry);
840         return filelist;
843 static struct filelist *
844 mpdclient_recv_filelist_response(struct mpdclient *c)
846         struct filelist *filelist;
847         struct mpd_entity *entity;
849         filelist = filelist_new();
851         while ((entity = mpd_recv_entity(c->connection)) != NULL)
852                 filelist_append(filelist, entity);
854         if (mpdclient_finish_command(c)) {
855                 filelist_free(filelist);
856                 return NULL;
857         }
859         return filelist;
862 struct filelist *
863 mpdclient_filelist_search(struct mpdclient *c,
864                           int exact_match,
865                           enum mpd_tag_type tag,
866                           gchar *filter_utf8)
868         if (MPD_ERROR(c))
869                 return NULL;
871         mpd_search_db_songs(c->connection, exact_match);
872         mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
873                                       tag, filter_utf8);
874         mpd_search_commit(c->connection);
876         return mpdclient_recv_filelist_response(c);
879 int
880 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
882         guint i;
884         if (MPD_ERROR(c))
885                 return -1;
887         if (filelist_is_empty(fl))
888                 return 0;
890         mpd_command_list_begin(c->connection, false);
892         for (i = 0; i < filelist_length(fl); ++i) {
893                 struct filelist_entry *entry = filelist_get(fl, i);
894                 struct mpd_entity *entity  = entry->entity;
896                 if (entity != NULL &&
897                     mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
898                         const struct mpd_song *song =
899                                 mpd_entity_get_song(entity);
900                         const char *uri = mpd_song_get_uri(song);
902                         if (uri != NULL)
903                                 mpd_send_add(c->connection, uri);
904                 }
905         }
907         mpd_command_list_end(c->connection);
908         return mpdclient_finish_command(c);
911 GList *
912 mpdclient_get_artists(struct mpdclient *c)
914         GList *list = NULL;
915         struct mpd_pair *pair;
917         if (MPD_ERROR(c))
918                return NULL;
920         mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
921         mpd_search_commit(c->connection);
923         while ((pair = mpd_recv_pair_tag(c->connection,
924                                          MPD_TAG_ARTIST)) != NULL) {
925                 list = g_list_append(list, g_strdup(pair->value));
926                 mpd_return_pair(c->connection, pair);
927         }
929         if (mpdclient_finish_command(c))
930                 return string_list_free(list);
932         return list;
935 GList *
936 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
938         GList *list = NULL;
939         struct mpd_pair *pair;
941         if (MPD_ERROR(c))
942                return NULL;
944         mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
945         if (artist_utf8 != NULL)
946                 mpd_search_add_tag_constraint(c->connection,
947                                               MPD_OPERATOR_DEFAULT,
948                                               MPD_TAG_ARTIST, artist_utf8);
949         mpd_search_commit(c->connection);
951         while ((pair = mpd_recv_pair_tag(c->connection,
952                                          MPD_TAG_ALBUM)) != NULL) {
953                 list = g_list_append(list, g_strdup(pair->value));
954                 mpd_return_pair(c->connection, pair);
955         }
957         if (mpdclient_finish_command(c))
958                 return string_list_free(list);
960         return list;