Code

mpdclient: removed several functions
[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_clear(struct mpdclient *c)
320         gint retval = 0;
322         if (MPD_ERROR(c))
323                 return -1;
325         mpd_send_clear(c->connection);
326         retval = mpdclient_finish_command(c);
328         if (retval)
329                 c->events |= MPD_IDLE_PLAYLIST;
331         return retval;
334 gint
335 mpdclient_cmd_volume(struct mpdclient *c, gint value)
337         if (MPD_ERROR(c))
338                 return -1;
340         mpd_send_set_volume(c->connection, value);
341         return mpdclient_finish_command(c);
344 gint mpdclient_cmd_volume_up(struct mpdclient *c)
346         if (MPD_ERROR(c))
347                 return -1;
349         if (c->status == NULL ||
350             mpd_status_get_volume(c->status) == -1)
351                 return 0;
353         if (c->volume < 0)
354                 c->volume = mpd_status_get_volume(c->status);
356         if (c->volume >= 100)
357                 return 0;
359         return mpdclient_cmd_volume(c, ++c->volume);
362 gint mpdclient_cmd_volume_down(struct mpdclient *c)
364         if (MPD_ERROR(c))
365                 return -1;
367         if (c->status == NULL || mpd_status_get_volume(c->status) < 0)
368                 return 0;
370         if (c->volume < 0)
371                 c->volume = mpd_status_get_volume(c->status);
373         if (c->volume <= 0)
374                 return 0;
376         return mpdclient_cmd_volume(c, --c->volume);
379 gint
380 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
382         if (MPD_ERROR(c))
383                 return -1;
385         mpd_send_add(c->connection, path_utf8);
386         return mpdclient_finish_command(c);
389 gint
390 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
392         struct mpd_status *status;
393         struct mpd_song *new_song;
395         assert(c != NULL);
396         assert(song != NULL);
398         if (MPD_ERROR(c) || c->status == NULL)
399                 return -1;
401         /* send the add command to mpd; at the same time, get the new
402            status (to verify the new playlist id) and the last song
403            (we hope that's the song we just added) */
405         if (!mpd_command_list_begin(c->connection, true) ||
406             !mpd_send_add(c->connection, mpd_song_get_uri(song)) ||
407             !mpd_send_status(c->connection) ||
408             !mpd_send_get_queue_song_pos(c->connection,
409                                          playlist_length(&c->playlist)) ||
410             !mpd_command_list_end(c->connection) ||
411             !mpd_response_next(c->connection))
412                 return mpdclient_handle_error(c);
414         c->events |= MPD_IDLE_PLAYLIST;
416         status = mpd_recv_status(c->connection);
417         if (status != NULL) {
418                 if (c->status != NULL)
419                         mpd_status_free(c->status);
420                 c->status = status;
421         }
423         if (!mpd_response_next(c->connection))
424                 return mpdclient_handle_error(c);
426         new_song = mpd_recv_song(c->connection);
427         if (!mpd_response_finish(c->connection) || new_song == NULL) {
428                 if (new_song != NULL)
429                         mpd_song_free(new_song);
431                 return mpd_connection_clear_error(c->connection)
432                         ? 0 : mpdclient_handle_error(c);
433         }
435         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) + 1 &&
436             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
437                 /* the cheap route: match on the new playlist length
438                    and its version, we can keep our local playlist
439                    copy in sync */
440                 c->playlist.version = mpd_status_get_queue_version(status);
442                 /* the song we just received has the correct id;
443                    append it to the local playlist */
444                 playlist_append(&c->playlist, new_song);
445         }
447         mpd_song_free(new_song);
449         return -0;
452 gint
453 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
455         const struct mpd_song *song;
456         struct mpd_status *status;
458         if (MPD_ERROR(c) || c->status == NULL)
459                 return -1;
461         if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
462                 return -1;
464         song = playlist_get(&c->playlist, idx);
466         /* send the delete command to mpd; at the same time, get the
467            new status (to verify the playlist id) */
469         if (!mpd_command_list_begin(c->connection, false) ||
470             !mpd_send_delete_id(c->connection, mpd_song_get_id(song)) ||
471             !mpd_send_status(c->connection) ||
472             !mpd_command_list_end(c->connection))
473                 return mpdclient_handle_error(c);
475         c->events |= MPD_IDLE_PLAYLIST;
477         status = mpd_recv_status(c->connection);
478         if (status != NULL) {
479                 if (c->status != NULL)
480                         mpd_status_free(c->status);
481                 c->status = status;
482         }
484         if (!mpd_response_finish(c->connection))
485                 return mpdclient_handle_error(c);
487         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - 1 &&
488             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
489                 /* the cheap route: match on the new playlist length
490                    and its version, we can keep our local playlist
491                    copy in sync */
492                 c->playlist.version = mpd_status_get_queue_version(status);
494                 /* remove the song from the local playlist */
495                 playlist_remove(&c->playlist, idx);
497                 /* remove references to the song */
498                 if (c->song == song)
499                         c->song = NULL;
500         }
502         return 0;
505 /**
506  * Fallback for mpdclient_cmd_delete_range() on MPD older than 0.16.
507  * It emulates the "delete range" command with a list of simple
508  * "delete" commands.
509  */
510 static gint
511 mpdclient_cmd_delete_range_fallback(struct mpdclient *c,
512                                     unsigned start, unsigned end)
514         if (!mpd_command_list_begin(c->connection, false))
515                 return mpdclient_handle_error(c);
517         for (; start < end; --end)
518                 mpd_send_delete(c->connection, start);
520         if (!mpd_command_list_end(c->connection) ||
521             !mpd_response_finish(c->connection))
522                 return mpdclient_handle_error(c);
524         return 0;
527 gint
528 mpdclient_cmd_delete_range(struct mpdclient *c, unsigned start, unsigned end)
530         struct mpd_status *status;
532         if (MPD_ERROR(c))
533                 return -1;
535         if (mpd_connection_cmp_server_version(c->connection, 0, 16, 0) < 0)
536                 return mpdclient_cmd_delete_range_fallback(c, start, end);
538         /* MPD 0.16 supports "delete" with a range argument */
540         /* send the delete command to mpd; at the same time, get the
541            new status (to verify the playlist id) */
543         if (!mpd_command_list_begin(c->connection, false) ||
544             !mpd_send_delete_range(c->connection, start, end) ||
545             !mpd_send_status(c->connection) ||
546             !mpd_command_list_end(c->connection))
547                 return mpdclient_handle_error(c);
549         c->events |= MPD_IDLE_PLAYLIST;
551         status = mpd_recv_status(c->connection);
552         if (status != NULL) {
553                 if (c->status != NULL)
554                         mpd_status_free(c->status);
555                 c->status = status;
556         }
558         if (!mpd_response_finish(c->connection))
559                 return mpdclient_handle_error(c);
561         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - (end - start) &&
562             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
563                 /* the cheap route: match on the new playlist length
564                    and its version, we can keep our local playlist
565                    copy in sync */
566                 c->playlist.version = mpd_status_get_queue_version(status);
568                 /* remove the song from the local playlist */
569                 while (end > start) {
570                         --end;
572                         /* remove references to the song */
573                         if (c->song == playlist_get(&c->playlist, end))
574                                 c->song = NULL;
576                         playlist_remove(&c->playlist, end);
577                 }
578         }
580         return 0;
583 gint
584 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
586         const struct mpd_song *song1, *song2;
587         struct mpd_status *status;
589         if (MPD_ERROR(c))
590                 return -1;
592         if (old_index == new_index || new_index < 0 ||
593             (guint)new_index >= c->playlist.list->len)
594                 return -1;
596         song1 = playlist_get(&c->playlist, old_index);
597         song2 = playlist_get(&c->playlist, new_index);
599         /* send the delete command to mpd; at the same time, get the
600            new status (to verify the playlist id) */
602         if (!mpd_command_list_begin(c->connection, false) ||
603             !mpd_send_swap_id(c->connection, mpd_song_get_id(song1),
604                               mpd_song_get_id(song2)) ||
605             !mpd_send_status(c->connection) ||
606             !mpd_command_list_end(c->connection))
607                 return mpdclient_handle_error(c);
609         c->events |= MPD_IDLE_PLAYLIST;
611         status = mpd_recv_status(c->connection);
612         if (status != NULL) {
613                 if (c->status != NULL)
614                         mpd_status_free(c->status);
615                 c->status = status;
616         }
618         if (!mpd_response_finish(c->connection))
619                 return mpdclient_handle_error(c);
621         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) &&
622             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
623                 /* the cheap route: match on the new playlist length
624                    and its version, we can keep our local playlist
625                    copy in sync */
626                 c->playlist.version = mpd_status_get_queue_version(status);
628                 /* swap songs in the local playlist */
629                 playlist_swap(&c->playlist, old_index, new_index);
630         }
632         return 0;
636 /****************************************************************************/
637 /*** Playlist management functions ******************************************/
638 /****************************************************************************/
640 /* update playlist */
641 bool
642 mpdclient_playlist_update(struct mpdclient *c)
644         struct mpd_entity *entity;
646         if (MPD_ERROR(c))
647                 return false;
649         playlist_clear(&c->playlist);
651         mpd_send_list_queue_meta(c->connection);
652         while ((entity = mpd_recv_entity(c->connection))) {
653                 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
654                         playlist_append(&c->playlist, mpd_entity_get_song(entity));
656                 mpd_entity_free(entity);
657         }
659         c->playlist.version = mpd_status_get_queue_version(c->status);
660         c->song = NULL;
662         return mpdclient_finish_command(c) == 0;
665 /* update playlist (plchanges) */
666 bool
667 mpdclient_playlist_update_changes(struct mpdclient *c)
669         struct mpd_song *song;
670         guint length;
672         if (MPD_ERROR(c))
673                 return false;
675         mpd_send_queue_changes_meta(c->connection, c->playlist.version);
677         while ((song = mpd_recv_song(c->connection)) != NULL) {
678                 int pos = mpd_song_get_pos(song);
680                 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
681                         /* update song */
682                         playlist_replace(&c->playlist, pos, song);
683                 } else {
684                         /* add a new song */
685                         playlist_append(&c->playlist, song);
686                 }
688                 mpd_song_free(song);
689         }
691         /* remove trailing songs */
693         length = mpd_status_get_queue_length(c->status);
694         while (length < c->playlist.list->len) {
695                 guint pos = c->playlist.list->len - 1;
697                 /* Remove the last playlist entry */
698                 playlist_remove(&c->playlist, pos);
699         }
701         c->song = NULL;
702         c->playlist.version = mpd_status_get_queue_version(c->status);
704         return mpdclient_finish_command(c) == 0;
708 /****************************************************************************/
709 /*** Filelist functions *****************************************************/
710 /****************************************************************************/
712 struct filelist *
713 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
715         struct filelist *filelist;
716         struct mpd_entity *entity;
718         if (MPD_ERROR(c))
719                 return NULL;
721         mpd_send_list_meta(c->connection, path);
722         filelist = filelist_new();
724         while ((entity = mpd_recv_entity(c->connection)) != NULL)
725                 filelist_append(filelist, entity);
727         if (mpdclient_finish_command(c)) {
728                 filelist_free(filelist);
729                 return NULL;
730         }
732         filelist_sort_dir_play(filelist, compare_filelistentry);
734         return filelist;
737 static struct filelist *
738 mpdclient_recv_filelist_response(struct mpdclient *c)
740         struct filelist *filelist;
741         struct mpd_entity *entity;
743         filelist = filelist_new();
745         while ((entity = mpd_recv_entity(c->connection)) != NULL)
746                 filelist_append(filelist, entity);
748         if (mpdclient_finish_command(c)) {
749                 filelist_free(filelist);
750                 return NULL;
751         }
753         return filelist;
756 struct filelist *
757 mpdclient_filelist_search(struct mpdclient *c,
758                           int exact_match,
759                           enum mpd_tag_type tag,
760                           gchar *filter_utf8)
762         if (MPD_ERROR(c))
763                 return NULL;
765         mpd_search_db_songs(c->connection, exact_match);
766         mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
767                                       tag, filter_utf8);
768         mpd_search_commit(c->connection);
770         return mpdclient_recv_filelist_response(c);
773 int
774 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
776         guint i;
778         if (MPD_ERROR(c))
779                 return -1;
781         if (filelist_is_empty(fl))
782                 return 0;
784         mpd_command_list_begin(c->connection, false);
786         for (i = 0; i < filelist_length(fl); ++i) {
787                 struct filelist_entry *entry = filelist_get(fl, i);
788                 struct mpd_entity *entity  = entry->entity;
790                 if (entity != NULL &&
791                     mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
792                         const struct mpd_song *song =
793                                 mpd_entity_get_song(entity);
794                         const char *uri = mpd_song_get_uri(song);
796                         if (uri != NULL)
797                                 mpd_send_add(c->connection, uri);
798                 }
799         }
801         mpd_command_list_end(c->connection);
802         return mpdclient_finish_command(c);
805 GList *
806 mpdclient_get_artists(struct mpdclient *c)
808         GList *list = NULL;
809         struct mpd_pair *pair;
811         if (MPD_ERROR(c))
812                return NULL;
814         mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
815         mpd_search_commit(c->connection);
817         while ((pair = mpd_recv_pair_tag(c->connection,
818                                          MPD_TAG_ARTIST)) != NULL) {
819                 list = g_list_append(list, g_strdup(pair->value));
820                 mpd_return_pair(c->connection, pair);
821         }
823         if (mpdclient_finish_command(c))
824                 return string_list_free(list);
826         return list;
829 GList *
830 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
832         GList *list = NULL;
833         struct mpd_pair *pair;
835         if (MPD_ERROR(c))
836                return NULL;
838         mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
839         if (artist_utf8 != NULL)
840                 mpd_search_add_tag_constraint(c->connection,
841                                               MPD_OPERATOR_DEFAULT,
842                                               MPD_TAG_ARTIST, artist_utf8);
843         mpd_search_commit(c->connection);
845         while ((pair = mpd_recv_pair_tag(c->connection,
846                                          MPD_TAG_ALBUM)) != NULL) {
847                 list = g_list_append(list, g_strdup(pair->value));
848                 mpd_return_pair(c->connection, pair);
849         }
851         if (mpdclient_finish_command(c))
852                 return string_list_free(list);
854         return list;