Code

mpdclient: rewrite the "fancy swap" code
[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 #define BUFSIZE 1024
37 static bool
38 MPD_ERROR(const struct mpdclient *client)
39 {
40         return client->connection == NULL ||
41                 mpd_connection_get_error(client->connection) != MPD_ERROR_SUCCESS;
42 }
44 /* filelist sorting functions */
45 static gint
46 compare_filelistentry(gconstpointer filelist_entry1,
47                           gconstpointer filelist_entry2)
48 {
49         const struct mpd_entity *e1, *e2;
50         int n = 0;
52         e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
53         e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
55         if (e1 != NULL && e2 != NULL &&
56             mpd_entity_get_type(e1) == mpd_entity_get_type(e2)) {
57                 switch (mpd_entity_get_type(e1)) {
58                 case MPD_ENTITY_TYPE_UNKNOWN:
59                         break;
60                 case MPD_ENTITY_TYPE_DIRECTORY:
61                         n = g_utf8_collate(mpd_directory_get_path(mpd_entity_get_directory(e1)),
62                                            mpd_directory_get_path(mpd_entity_get_directory(e2)));
63                         break;
64                 case MPD_ENTITY_TYPE_SONG:
65                         break;
66                 case MPD_ENTITY_TYPE_PLAYLIST:
67                         n = g_utf8_collate(mpd_playlist_get_path(mpd_entity_get_playlist(e1)),
68                                            mpd_playlist_get_path(mpd_entity_get_playlist(e2)));
69                 }
70         }
71         return n;
72 }
74 /* sort by list-format */
75 gint
76 compare_filelistentry_format(gconstpointer filelist_entry1,
77                              gconstpointer filelist_entry2)
78 {
79         const struct mpd_entity *e1, *e2;
80         char key1[BUFSIZE], key2[BUFSIZE];
81         int n = 0;
83         e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
84         e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
86         if (e1 && e2 &&
87             mpd_entity_get_type(e1) == MPD_ENTITY_TYPE_SONG &&
88             mpd_entity_get_type(e2) == MPD_ENTITY_TYPE_SONG) {
89                 strfsong(key1, BUFSIZE, options.list_format, mpd_entity_get_song(e1));
90                 strfsong(key2, BUFSIZE, options.list_format, mpd_entity_get_song(e2));
91                 n = strcmp(key1,key2);
92         }
94         return n;
95 }
98 /****************************************************************************/
99 /*** mpdclient functions ****************************************************/
100 /****************************************************************************/
102 gint
103 mpdclient_handle_error(struct mpdclient *c)
105         enum mpd_error error = mpd_connection_get_error(c->connection);
107         assert(error != MPD_ERROR_SUCCESS);
109         if (error == MPD_ERROR_SERVER &&
110             mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION &&
111             screen_auth(c))
112                 return 0;
114         if (error == MPD_ERROR_SERVER)
115                 error = error | (mpd_connection_get_server_error(c->connection) << 8);
117         mpdclient_ui_error(mpd_connection_get_error_message(c->connection));
119         if (!mpd_connection_clear_error(c->connection))
120                 mpdclient_disconnect(c);
122         return error;
125 static gint
126 mpdclient_finish_command(struct mpdclient *c)
128         return mpd_response_finish(c->connection)
129                 ? 0 : mpdclient_handle_error(c);
132 struct mpdclient *
133 mpdclient_new(void)
135         struct mpdclient *c;
137         c = g_new0(struct mpdclient, 1);
138         playlist_init(&c->playlist);
139         c->volume = -1;
140         c->events = 0;
142         return c;
145 void
146 mpdclient_free(struct mpdclient *c)
148         mpdclient_disconnect(c);
150         mpdclient_playlist_free(&c->playlist);
152         g_free(c);
155 void
156 mpdclient_disconnect(struct mpdclient *c)
158         if (c->connection)
159                 mpd_connection_free(c->connection);
160         c->connection = NULL;
162         if (c->status)
163                 mpd_status_free(c->status);
164         c->status = NULL;
166         playlist_clear(&c->playlist);
168         if (c->song)
169                 c->song = NULL;
172 bool
173 mpdclient_connect(struct mpdclient *c,
174                   const gchar *host,
175                   gint port,
176                   gfloat _timeout,
177                   const gchar *password)
179         /* close any open connection */
180         if( c->connection )
181                 mpdclient_disconnect(c);
183         /* connect to MPD */
184         c->connection = mpd_connection_new(host, port, _timeout * 1000);
185         if (c->connection == NULL)
186                 g_error("Out of memory");
188         if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
189                 mpdclient_handle_error(c);
190                 mpdclient_disconnect(c);
191                 return false;
192         }
194         /* send password */
195         if (password != NULL && !mpd_run_password(c->connection, password)) {
196                 mpdclient_handle_error(c);
197                 mpdclient_disconnect(c);
198                 return false;
199         }
201         return true;
204 bool
205 mpdclient_update(struct mpdclient *c)
207         bool retval;
209         c->volume = -1;
211         if (MPD_ERROR(c))
212                 return false;
214         /* always announce these options as long as we don't have real
215            "idle" support */
216         c->events |= MPD_IDLE_PLAYER|MPD_IDLE_OPTIONS;
218         /* free the old status */
219         if (c->status)
220                 mpd_status_free(c->status);
222         /* retrieve new status */
223         c->status = mpd_run_status(c->connection);
224         if (c->status == NULL)
225                 return mpdclient_handle_error(c) == 0;
227         if (c->update_id != mpd_status_get_update_id(c->status)) {
228                 c->events |= MPD_IDLE_UPDATE;
230                 if (c->update_id > 0)
231                         c->events |= MPD_IDLE_DATABASE;
232         }
234         c->update_id = mpd_status_get_update_id(c->status);
236         if (c->volume != mpd_status_get_volume(c->status))
237                 c->events |= MPD_IDLE_MIXER;
239         c->volume = mpd_status_get_volume(c->status);
241         /* check if the playlist needs an update */
242         if (c->playlist.version != mpd_status_get_queue_version(c->status)) {
243                 c->events |= MPD_IDLE_PLAYLIST;
245                 if (!playlist_is_empty(&c->playlist))
246                         retval = mpdclient_playlist_update_changes(c);
247                 else
248                         retval = mpdclient_playlist_update(c);
249         } else
250                 retval = true;
252         /* update the current song */
253         if (!c->song || mpd_status_get_song_id(c->status)) {
254                 c->song = playlist_get_song(&c->playlist,
255                                             mpd_status_get_song_pos(c->status));
256         }
258         return retval;
262 /****************************************************************************/
263 /*** MPD Commands  **********************************************************/
264 /****************************************************************************/
266 gint
267 mpdclient_cmd_play(struct mpdclient *c, gint idx)
269         const struct mpd_song *song = playlist_get_song(&c->playlist, idx);
271         if (MPD_ERROR(c))
272                 return -1;
274         if (song)
275                 mpd_send_play_id(c->connection, mpd_song_get_id(song));
276         else
277                 mpd_send_play(c->connection);
279         return mpdclient_finish_command(c);
282 gint
283 mpdclient_cmd_crop(struct mpdclient *c)
285         struct mpd_status *status;
286         bool playing;
287         int length, current;
289         if (MPD_ERROR(c))
290                 return -1;
292         status = mpd_run_status(c->connection);
293         if (status == NULL)
294                 return mpdclient_handle_error(c);
296         playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
297                 mpd_status_get_state(status) == MPD_STATE_PAUSE;
298         length = mpd_status_get_queue_length(status);
299         current = mpd_status_get_song_pos(status);
301         mpd_status_free(status);
303         if (!playing || length < 2)
304                 return 0;
306         mpd_command_list_begin(c->connection, false);
308         while (--length >= 0)
309                 if (length != current)
310                         mpd_send_delete(c->connection, length);
312         mpd_command_list_end(c->connection);
314         return mpdclient_finish_command(c);
317 gint
318 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
320         mpd_send_shuffle_range(c->connection, start, end);
321         return mpdclient_finish_command(c);
324 gint
325 mpdclient_cmd_clear(struct mpdclient *c)
327         gint retval = 0;
329         if (MPD_ERROR(c))
330                 return -1;
332         mpd_send_clear(c->connection);
333         retval = mpdclient_finish_command(c);
335         if (retval)
336                 c->events |= MPD_IDLE_PLAYLIST;
338         return retval;
341 gint
342 mpdclient_cmd_volume(struct mpdclient *c, gint value)
344         if (MPD_ERROR(c))
345                 return -1;
347         mpd_send_set_volume(c->connection, value);
348         return mpdclient_finish_command(c);
351 gint mpdclient_cmd_volume_up(struct mpdclient *c)
353         if (MPD_ERROR(c))
354                 return -1;
356         if (c->status == NULL ||
357             mpd_status_get_volume(c->status) == -1)
358                 return 0;
360         if (c->volume < 0)
361                 c->volume = mpd_status_get_volume(c->status);
363         if (c->volume >= 100)
364                 return 0;
366         return mpdclient_cmd_volume(c, ++c->volume);
369 gint mpdclient_cmd_volume_down(struct mpdclient *c)
371         if (MPD_ERROR(c))
372                 return -1;
374         if (c->status == NULL || mpd_status_get_volume(c->status) < 0)
375                 return 0;
377         if (c->volume < 0)
378                 c->volume = mpd_status_get_volume(c->status);
380         if (c->volume <= 0)
381                 return 0;
383         return mpdclient_cmd_volume(c, --c->volume);
386 gint
387 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
389         if (MPD_ERROR(c))
390                 return -1;
392         mpd_send_add(c->connection, path_utf8);
393         return mpdclient_finish_command(c);
396 gint
397 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
399         struct mpd_status *status;
400         struct mpd_song *new_song;
402         assert(c != NULL);
403         assert(song != NULL);
405         if (MPD_ERROR(c) || c->status == NULL)
406                 return -1;
408         /* send the add command to mpd; at the same time, get the new
409            status (to verify the new playlist id) and the last song
410            (we hope that's the song we just added) */
412         if (!mpd_command_list_begin(c->connection, true) ||
413             !mpd_send_add(c->connection, mpd_song_get_uri(song)) ||
414             !mpd_send_status(c->connection) ||
415             !mpd_send_get_queue_song_pos(c->connection,
416                                          playlist_length(&c->playlist)) ||
417             !mpd_command_list_end(c->connection) ||
418             !mpd_response_next(c->connection))
419                 return mpdclient_handle_error(c);
421         c->events |= MPD_IDLE_PLAYLIST;
423         status = mpd_recv_status(c->connection);
424         if (status != NULL) {
425                 if (c->status != NULL)
426                         mpd_status_free(c->status);
427                 c->status = status;
428         }
430         if (!mpd_response_next(c->connection))
431                 return mpdclient_handle_error(c);
433         new_song = mpd_recv_song(c->connection);
434         if (!mpd_response_finish(c->connection) || new_song == NULL) {
435                 if (new_song != NULL)
436                         mpd_song_free(new_song);
438                 return mpd_connection_clear_error(c->connection)
439                         ? 0 : mpdclient_handle_error(c);
440         }
442         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) + 1 &&
443             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
444                 /* the cheap route: match on the new playlist length
445                    and its version, we can keep our local playlist
446                    copy in sync */
447                 c->playlist.version = mpd_status_get_queue_version(status);
449                 /* the song we just received has the correct id;
450                    append it to the local playlist */
451                 playlist_append(&c->playlist, new_song);
452         }
454         mpd_song_free(new_song);
456         return -0;
459 gint
460 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
462         const struct mpd_song *song;
463         struct mpd_status *status;
465         if (MPD_ERROR(c) || c->status == NULL)
466                 return -1;
468         if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
469                 return -1;
471         song = playlist_get(&c->playlist, idx);
473         /* send the delete command to mpd; at the same time, get the
474            new status (to verify the playlist id) */
476         if (!mpd_command_list_begin(c->connection, false) ||
477             !mpd_send_delete_id(c->connection, mpd_song_get_id(song)) ||
478             !mpd_send_status(c->connection) ||
479             !mpd_command_list_end(c->connection))
480                 return mpdclient_handle_error(c);
482         c->events |= MPD_IDLE_PLAYLIST;
484         status = mpd_recv_status(c->connection);
485         if (status != NULL) {
486                 if (c->status != NULL)
487                         mpd_status_free(c->status);
488                 c->status = status;
489         }
491         if (!mpd_response_finish(c->connection))
492                 return mpdclient_handle_error(c);
494         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - 1 &&
495             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
496                 /* the cheap route: match on the new playlist length
497                    and its version, we can keep our local playlist
498                    copy in sync */
499                 c->playlist.version = mpd_status_get_queue_version(status);
501                 /* remove the song from the local playlist */
502                 playlist_remove(&c->playlist, idx);
504                 /* remove references to the song */
505                 if (c->song == song)
506                         c->song = NULL;
507         }
509         return 0;
512 /**
513  * Fallback for mpdclient_cmd_delete_range() on MPD older than 0.16.
514  * It emulates the "delete range" command with a list of simple
515  * "delete" commands.
516  */
517 static gint
518 mpdclient_cmd_delete_range_fallback(struct mpdclient *c,
519                                     unsigned start, unsigned end)
521         if (!mpd_command_list_begin(c->connection, false))
522                 return mpdclient_handle_error(c);
524         for (; start < end; --end)
525                 mpd_send_delete(c->connection, start);
527         if (!mpd_command_list_end(c->connection) ||
528             !mpd_response_finish(c->connection))
529                 return mpdclient_handle_error(c);
531         return 0;
534 gint
535 mpdclient_cmd_delete_range(struct mpdclient *c, unsigned start, unsigned end)
537         struct mpd_status *status;
539         if (MPD_ERROR(c))
540                 return -1;
542         if (mpd_connection_cmp_server_version(c->connection, 0, 16, 0) < 0)
543                 return mpdclient_cmd_delete_range_fallback(c, start, end);
545         /* MPD 0.16 supports "delete" with a range argument */
547         /* send the delete command to mpd; at the same time, get the
548            new status (to verify the playlist id) */
550         if (!mpd_command_list_begin(c->connection, false) ||
551             !mpd_send_delete_range(c->connection, start, end) ||
552             !mpd_send_status(c->connection) ||
553             !mpd_command_list_end(c->connection))
554                 return mpdclient_handle_error(c);
556         c->events |= MPD_IDLE_PLAYLIST;
558         status = mpd_recv_status(c->connection);
559         if (status != NULL) {
560                 if (c->status != NULL)
561                         mpd_status_free(c->status);
562                 c->status = status;
563         }
565         if (!mpd_response_finish(c->connection))
566                 return mpdclient_handle_error(c);
568         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - (end - start) &&
569             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
570                 /* the cheap route: match on the new playlist length
571                    and its version, we can keep our local playlist
572                    copy in sync */
573                 c->playlist.version = mpd_status_get_queue_version(status);
575                 /* remove the song from the local playlist */
576                 while (end > start) {
577                         --end;
579                         /* remove references to the song */
580                         if (c->song == playlist_get(&c->playlist, end))
581                                 c->song = NULL;
583                         playlist_remove(&c->playlist, end);
584                 }
585         }
587         return 0;
590 gint
591 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
593         const struct mpd_song *song1, *song2;
594         struct mpd_status *status;
596         if (MPD_ERROR(c))
597                 return -1;
599         if (old_index == new_index || new_index < 0 ||
600             (guint)new_index >= c->playlist.list->len)
601                 return -1;
603         song1 = playlist_get(&c->playlist, old_index);
604         song2 = playlist_get(&c->playlist, new_index);
606         /* send the delete command to mpd; at the same time, get the
607            new status (to verify the playlist id) */
609         if (!mpd_command_list_begin(c->connection, false) ||
610             !mpd_send_swap_id(c->connection, mpd_song_get_id(song1),
611                               mpd_song_get_id(song2)) ||
612             !mpd_send_status(c->connection) ||
613             !mpd_command_list_end(c->connection))
614                 return mpdclient_handle_error(c);
616         c->events |= MPD_IDLE_PLAYLIST;
618         status = mpd_recv_status(c->connection);
619         if (status != NULL) {
620                 if (c->status != NULL)
621                         mpd_status_free(c->status);
622                 c->status = status;
623         }
625         if (!mpd_response_finish(c->connection))
626                 return mpdclient_handle_error(c);
628         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) &&
629             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
630                 /* the cheap route: match on the new playlist length
631                    and its version, we can keep our local playlist
632                    copy in sync */
633                 c->playlist.version = mpd_status_get_queue_version(status);
635                 /* swap songs in the local playlist */
636                 playlist_swap(&c->playlist, old_index, new_index);
637         }
639         return 0;
642 gint
643 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
645         gint retval = 0;
647         if (MPD_ERROR(c))
648                 return -1;
650         mpd_send_save(c->connection, filename_utf8);
651         if ((retval = mpdclient_finish_command(c)) == 0) {
652                 c->events |= MPD_IDLE_STORED_PLAYLIST;
653         }
655         return retval;
658 gint
659 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
661         if (MPD_ERROR(c))
662                 return -1;
664         mpd_send_load(c->connection, filename_utf8);
665         return mpdclient_finish_command(c);
668 gint
669 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
671         gint retval = 0;
673         if (MPD_ERROR(c))
674                 return -1;
676         mpd_send_rm(c->connection, filename_utf8);
677         if ((retval = mpdclient_finish_command(c)) == 0)
678                 c->events |= MPD_IDLE_STORED_PLAYLIST;
680         return retval;
684 /****************************************************************************/
685 /*** Playlist management functions ******************************************/
686 /****************************************************************************/
688 /* update playlist */
689 bool
690 mpdclient_playlist_update(struct mpdclient *c)
692         struct mpd_entity *entity;
694         if (MPD_ERROR(c))
695                 return false;
697         playlist_clear(&c->playlist);
699         mpd_send_list_queue_meta(c->connection);
700         while ((entity = mpd_recv_entity(c->connection))) {
701                 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
702                         playlist_append(&c->playlist, mpd_entity_get_song(entity));
704                 mpd_entity_free(entity);
705         }
707         c->playlist.version = mpd_status_get_queue_version(c->status);
708         c->song = NULL;
710         return mpdclient_finish_command(c) == 0;
713 /* update playlist (plchanges) */
714 bool
715 mpdclient_playlist_update_changes(struct mpdclient *c)
717         struct mpd_song *song;
718         guint length;
720         if (MPD_ERROR(c))
721                 return false;
723         mpd_send_queue_changes_meta(c->connection, c->playlist.version);
725         while ((song = mpd_recv_song(c->connection)) != NULL) {
726                 int pos = mpd_song_get_pos(song);
728                 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
729                         /* update song */
730                         playlist_replace(&c->playlist, pos, song);
731                 } else {
732                         /* add a new song */
733                         playlist_append(&c->playlist, song);
734                 }
736                 mpd_song_free(song);
737         }
739         /* remove trailing songs */
741         length = mpd_status_get_queue_length(c->status);
742         while (length < c->playlist.list->len) {
743                 guint pos = c->playlist.list->len - 1;
745                 /* Remove the last playlist entry */
746                 playlist_remove(&c->playlist, pos);
747         }
749         c->song = NULL;
750         c->playlist.version = mpd_status_get_queue_version(c->status);
752         return mpdclient_finish_command(c) == 0;
756 /****************************************************************************/
757 /*** Filelist functions *****************************************************/
758 /****************************************************************************/
760 struct filelist *
761 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
763         struct filelist *filelist;
764         struct mpd_entity *entity;
766         if (MPD_ERROR(c))
767                 return NULL;
769         mpd_send_list_meta(c->connection, path);
770         filelist = filelist_new();
772         while ((entity = mpd_recv_entity(c->connection)) != NULL)
773                 filelist_append(filelist, entity);
775         if (mpdclient_finish_command(c)) {
776                 filelist_free(filelist);
777                 return NULL;
778         }
780         filelist_sort_dir_play(filelist, compare_filelistentry);
782         return filelist;
785 static struct filelist *
786 mpdclient_recv_filelist_response(struct mpdclient *c)
788         struct filelist *filelist;
789         struct mpd_entity *entity;
791         filelist = filelist_new();
793         while ((entity = mpd_recv_entity(c->connection)) != NULL)
794                 filelist_append(filelist, entity);
796         if (mpdclient_finish_command(c)) {
797                 filelist_free(filelist);
798                 return NULL;
799         }
801         return filelist;
804 struct filelist *
805 mpdclient_filelist_search(struct mpdclient *c,
806                           int exact_match,
807                           enum mpd_tag_type tag,
808                           gchar *filter_utf8)
810         if (MPD_ERROR(c))
811                 return NULL;
813         mpd_search_db_songs(c->connection, exact_match);
814         mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
815                                       tag, filter_utf8);
816         mpd_search_commit(c->connection);
818         return mpdclient_recv_filelist_response(c);
821 int
822 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
824         guint i;
826         if (MPD_ERROR(c))
827                 return -1;
829         if (filelist_is_empty(fl))
830                 return 0;
832         mpd_command_list_begin(c->connection, false);
834         for (i = 0; i < filelist_length(fl); ++i) {
835                 struct filelist_entry *entry = filelist_get(fl, i);
836                 struct mpd_entity *entity  = entry->entity;
838                 if (entity != NULL &&
839                     mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
840                         const struct mpd_song *song =
841                                 mpd_entity_get_song(entity);
842                         const char *uri = mpd_song_get_uri(song);
844                         if (uri != NULL)
845                                 mpd_send_add(c->connection, uri);
846                 }
847         }
849         mpd_command_list_end(c->connection);
850         return mpdclient_finish_command(c);
853 GList *
854 mpdclient_get_artists(struct mpdclient *c)
856         GList *list = NULL;
857         struct mpd_pair *pair;
859         if (MPD_ERROR(c))
860                return NULL;
862         mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
863         mpd_search_commit(c->connection);
865         while ((pair = mpd_recv_pair_tag(c->connection,
866                                          MPD_TAG_ARTIST)) != NULL) {
867                 list = g_list_append(list, g_strdup(pair->value));
868                 mpd_return_pair(c->connection, pair);
869         }
871         if (mpdclient_finish_command(c))
872                 return string_list_free(list);
874         return list;
877 GList *
878 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
880         GList *list = NULL;
881         struct mpd_pair *pair;
883         if (MPD_ERROR(c))
884                return NULL;
886         mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
887         if (artist_utf8 != NULL)
888                 mpd_search_add_tag_constraint(c->connection,
889                                               MPD_OPERATOR_DEFAULT,
890                                               MPD_TAG_ARTIST, artist_utf8);
891         mpd_search_commit(c->connection);
893         while ((pair = mpd_recv_pair_tag(c->connection,
894                                          MPD_TAG_ALBUM)) != NULL) {
895                 list = g_list_append(list, g_strdup(pair->value));
896                 mpd_return_pair(c->connection, pair);
897         }
899         if (mpdclient_finish_command(c))
900                 return string_list_free(list);
902         return list;