d60436946a378753538c1711cb653d0ce4bc9c16
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 ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
37 #define BUFSIZE 1024
39 static bool
40 MPD_ERROR(const struct mpdclient *client)
41 {
42 return client->connection == NULL ||
43 mpd_connection_get_error(client->connection) != MPD_ERROR_SUCCESS;
44 }
46 /* filelist sorting functions */
47 static gint
48 compare_filelistentry(gconstpointer filelist_entry1,
49 gconstpointer filelist_entry2)
50 {
51 const struct mpd_entity *e1, *e2;
52 int n = 0;
54 e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
55 e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
57 if (e1 != NULL && e2 != NULL &&
58 mpd_entity_get_type(e1) == mpd_entity_get_type(e2)) {
59 switch (mpd_entity_get_type(e1)) {
60 case MPD_ENTITY_TYPE_UNKNOWN:
61 break;
62 case MPD_ENTITY_TYPE_DIRECTORY:
63 n = g_utf8_collate(mpd_directory_get_path(mpd_entity_get_directory(e1)),
64 mpd_directory_get_path(mpd_entity_get_directory(e2)));
65 break;
66 case MPD_ENTITY_TYPE_SONG:
67 break;
68 case MPD_ENTITY_TYPE_PLAYLIST:
69 n = g_utf8_collate(mpd_playlist_get_path(mpd_entity_get_playlist(e1)),
70 mpd_playlist_get_path(mpd_entity_get_playlist(e2)));
71 }
72 }
73 return n;
74 }
76 /* sort by list-format */
77 gint
78 compare_filelistentry_format(gconstpointer filelist_entry1,
79 gconstpointer filelist_entry2)
80 {
81 const struct mpd_entity *e1, *e2;
82 char key1[BUFSIZE], key2[BUFSIZE];
83 int n = 0;
85 e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
86 e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
88 if (e1 && e2 &&
89 mpd_entity_get_type(e1) == MPD_ENTITY_TYPE_SONG &&
90 mpd_entity_get_type(e2) == MPD_ENTITY_TYPE_SONG) {
91 strfsong(key1, BUFSIZE, options.list_format, mpd_entity_get_song(e1));
92 strfsong(key2, BUFSIZE, options.list_format, mpd_entity_get_song(e2));
93 n = strcmp(key1,key2);
94 }
96 return n;
97 }
100 /****************************************************************************/
101 /*** mpdclient functions ****************************************************/
102 /****************************************************************************/
104 gint
105 mpdclient_handle_error(struct mpdclient *c)
106 {
107 enum mpd_error error = mpd_connection_get_error(c->connection);
109 assert(error != MPD_ERROR_SUCCESS);
111 if (error == MPD_ERROR_SERVER &&
112 mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION &&
113 screen_auth(c))
114 return 0;
116 if (error == MPD_ERROR_SERVER)
117 error = error | (mpd_connection_get_server_error(c->connection) << 8);
119 mpdclient_ui_error(mpd_connection_get_error_message(c->connection));
121 if (!mpd_connection_clear_error(c->connection))
122 mpdclient_disconnect(c);
124 return error;
125 }
127 static gint
128 mpdclient_finish_command(struct mpdclient *c)
129 {
130 return mpd_response_finish(c->connection)
131 ? 0 : mpdclient_handle_error(c);
132 }
134 struct mpdclient *
135 mpdclient_new(void)
136 {
137 struct mpdclient *c;
139 c = g_new0(struct mpdclient, 1);
140 playlist_init(&c->playlist);
141 c->volume = -1;
142 c->events = 0;
144 return c;
145 }
147 void
148 mpdclient_free(struct mpdclient *c)
149 {
150 mpdclient_disconnect(c);
152 mpdclient_playlist_free(&c->playlist);
154 g_free(c);
155 }
157 void
158 mpdclient_disconnect(struct mpdclient *c)
159 {
160 if (c->connection)
161 mpd_connection_free(c->connection);
162 c->connection = NULL;
164 if (c->status)
165 mpd_status_free(c->status);
166 c->status = NULL;
168 playlist_clear(&c->playlist);
170 if (c->song)
171 c->song = NULL;
172 }
174 bool
175 mpdclient_connect(struct mpdclient *c,
176 const gchar *host,
177 gint port,
178 gfloat _timeout,
179 const gchar *password)
180 {
181 /* close any open connection */
182 if( c->connection )
183 mpdclient_disconnect(c);
185 /* connect to MPD */
186 c->connection = mpd_connection_new(host, port, _timeout * 1000);
187 if (c->connection == NULL)
188 g_error("Out of memory");
190 if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
191 mpdclient_handle_error(c);
192 mpdclient_disconnect(c);
193 return false;
194 }
196 /* send password */
197 if (password != NULL && !mpd_run_password(c->connection, password)) {
198 mpdclient_handle_error(c);
199 mpdclient_disconnect(c);
200 return false;
201 }
203 return true;
204 }
206 bool
207 mpdclient_update(struct mpdclient *c)
208 {
209 bool retval;
211 c->volume = -1;
213 if (MPD_ERROR(c))
214 return false;
216 /* always announce these options as long as we don't have real
217 "idle" support */
218 c->events |= MPD_IDLE_PLAYER|MPD_IDLE_OPTIONS;
220 /* free the old status */
221 if (c->status)
222 mpd_status_free(c->status);
224 /* retrieve new status */
225 c->status = mpd_run_status(c->connection);
226 if (c->status == NULL)
227 return mpdclient_handle_error(c) == 0;
229 if (c->update_id != mpd_status_get_update_id(c->status)) {
230 c->events |= MPD_IDLE_UPDATE;
232 if (c->update_id > 0)
233 c->events |= MPD_IDLE_DATABASE;
234 }
236 c->update_id = mpd_status_get_update_id(c->status);
238 if (c->volume != mpd_status_get_volume(c->status))
239 c->events |= MPD_IDLE_MIXER;
241 c->volume = mpd_status_get_volume(c->status);
243 /* check if the playlist needs an update */
244 if (c->playlist.version != mpd_status_get_queue_version(c->status)) {
245 c->events |= MPD_IDLE_PLAYLIST;
247 if (!playlist_is_empty(&c->playlist))
248 retval = mpdclient_playlist_update_changes(c);
249 else
250 retval = mpdclient_playlist_update(c);
251 } else
252 retval = true;
254 /* update the current song */
255 if (!c->song || mpd_status_get_song_id(c->status)) {
256 c->song = playlist_get_song(&c->playlist,
257 mpd_status_get_song_pos(c->status));
258 }
260 return retval;
261 }
264 /****************************************************************************/
265 /*** MPD Commands **********************************************************/
266 /****************************************************************************/
268 gint
269 mpdclient_cmd_play(struct mpdclient *c, gint idx)
270 {
271 const struct mpd_song *song = playlist_get_song(&c->playlist, 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);
282 }
284 gint
285 mpdclient_cmd_crop(struct mpdclient *c)
286 {
287 struct mpd_status *status;
288 bool playing;
289 int length, current;
291 if (MPD_ERROR(c))
292 return -1;
294 status = mpd_run_status(c->connection);
295 if (status == NULL)
296 return mpdclient_handle_error(c);
298 playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
299 mpd_status_get_state(status) == MPD_STATE_PAUSE;
300 length = mpd_status_get_queue_length(status);
301 current = mpd_status_get_song_pos(status);
303 mpd_status_free(status);
305 if (!playing || length < 2)
306 return 0;
308 mpd_command_list_begin(c->connection, false);
310 while (--length >= 0)
311 if (length != current)
312 mpd_send_delete(c->connection, length);
314 mpd_command_list_end(c->connection);
316 return mpdclient_finish_command(c);
317 }
319 gint
320 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
321 {
322 mpd_send_shuffle_range(c->connection, start, end);
323 return mpdclient_finish_command(c);
324 }
326 gint
327 mpdclient_cmd_clear(struct mpdclient *c)
328 {
329 gint retval = 0;
331 if (MPD_ERROR(c))
332 return -1;
334 mpd_send_clear(c->connection);
335 retval = mpdclient_finish_command(c);
337 if (retval)
338 c->events |= MPD_IDLE_PLAYLIST;
340 return retval;
341 }
343 gint
344 mpdclient_cmd_volume(struct mpdclient *c, gint value)
345 {
346 if (MPD_ERROR(c))
347 return -1;
349 mpd_send_set_volume(c->connection, value);
350 return mpdclient_finish_command(c);
351 }
353 gint mpdclient_cmd_volume_up(struct mpdclient *c)
354 {
355 if (MPD_ERROR(c))
356 return -1;
358 if (c->status == NULL ||
359 mpd_status_get_volume(c->status) == -1)
360 return 0;
362 if (c->volume < 0)
363 c->volume = mpd_status_get_volume(c->status);
365 if (c->volume >= 100)
366 return 0;
368 return mpdclient_cmd_volume(c, ++c->volume);
369 }
371 gint mpdclient_cmd_volume_down(struct mpdclient *c)
372 {
373 if (MPD_ERROR(c))
374 return -1;
376 if (c->status == NULL || mpd_status_get_volume(c->status) < 0)
377 return 0;
379 if (c->volume < 0)
380 c->volume = mpd_status_get_volume(c->status);
382 if (c->volume <= 0)
383 return 0;
385 return mpdclient_cmd_volume(c, --c->volume);
386 }
388 gint
389 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
390 {
391 if (MPD_ERROR(c))
392 return -1;
394 mpd_send_add(c->connection, path_utf8);
395 return mpdclient_finish_command(c);
396 }
398 gint
399 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
400 {
401 struct mpd_status *status;
402 struct mpd_song *new_song;
404 assert(c != NULL);
405 assert(song != NULL);
407 if (MPD_ERROR(c) || c->status == NULL)
408 return -1;
410 /* send the add command to mpd; at the same time, get the new
411 status (to verify the new playlist id) and the last song
412 (we hope that's the song we just added) */
414 if (!mpd_command_list_begin(c->connection, true) ||
415 !mpd_send_add(c->connection, mpd_song_get_uri(song)) ||
416 !mpd_send_status(c->connection) ||
417 !mpd_send_get_queue_song_pos(c->connection,
418 playlist_length(&c->playlist)) ||
419 !mpd_command_list_end(c->connection) ||
420 !mpd_response_next(c->connection))
421 return mpdclient_handle_error(c);
423 c->events |= MPD_IDLE_PLAYLIST;
425 status = mpd_recv_status(c->connection);
426 if (status != NULL) {
427 if (c->status != NULL)
428 mpd_status_free(c->status);
429 c->status = status;
430 }
432 if (!mpd_response_next(c->connection))
433 return mpdclient_handle_error(c);
435 new_song = mpd_recv_song(c->connection);
436 if (!mpd_response_finish(c->connection) || new_song == NULL) {
437 if (new_song != NULL)
438 mpd_song_free(new_song);
440 return mpd_connection_clear_error(c->connection)
441 ? 0 : mpdclient_handle_error(c);
442 }
444 if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) + 1 &&
445 mpd_status_get_queue_version(status) == c->playlist.version + 1) {
446 /* the cheap route: match on the new playlist length
447 and its version, we can keep our local playlist
448 copy in sync */
449 c->playlist.version = mpd_status_get_queue_version(status);
451 /* the song we just received has the correct id;
452 append it to the local playlist */
453 playlist_append(&c->playlist, new_song);
454 }
456 mpd_song_free(new_song);
458 return -0;
459 }
461 gint
462 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
463 {
464 const struct mpd_song *song;
465 struct mpd_status *status;
467 if (MPD_ERROR(c) || c->status == NULL)
468 return -1;
470 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
471 return -1;
473 song = playlist_get(&c->playlist, idx);
475 /* send the delete command to mpd; at the same time, get the
476 new status (to verify the playlist id) */
478 if (!mpd_command_list_begin(c->connection, false) ||
479 !mpd_send_delete_id(c->connection, mpd_song_get_id(song)) ||
480 !mpd_send_status(c->connection) ||
481 !mpd_command_list_end(c->connection))
482 return mpdclient_handle_error(c);
484 c->events |= MPD_IDLE_PLAYLIST;
486 status = mpd_recv_status(c->connection);
487 if (status != NULL) {
488 if (c->status != NULL)
489 mpd_status_free(c->status);
490 c->status = status;
491 }
493 if (!mpd_response_finish(c->connection))
494 return mpdclient_handle_error(c);
496 if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - 1 &&
497 mpd_status_get_queue_version(status) == c->playlist.version + 1) {
498 /* the cheap route: match on the new playlist length
499 and its version, we can keep our local playlist
500 copy in sync */
501 c->playlist.version = mpd_status_get_queue_version(status);
503 /* remove the song from the local playlist */
504 playlist_remove(&c->playlist, idx);
506 /* remove references to the song */
507 if (c->song == song)
508 c->song = NULL;
509 }
511 return 0;
512 }
514 /**
515 * Fallback for mpdclient_cmd_delete_range() on MPD older than 0.16.
516 * It emulates the "delete range" command with a list of simple
517 * "delete" commands.
518 */
519 static gint
520 mpdclient_cmd_delete_range_fallback(struct mpdclient *c,
521 unsigned start, unsigned end)
522 {
523 if (!mpd_command_list_begin(c->connection, false))
524 return mpdclient_handle_error(c);
526 for (; start < end; --end)
527 mpd_send_delete(c->connection, start);
529 if (!mpd_command_list_end(c->connection) ||
530 !mpd_response_finish(c->connection))
531 return mpdclient_handle_error(c);
533 return 0;
534 }
536 gint
537 mpdclient_cmd_delete_range(struct mpdclient *c, unsigned start, unsigned end)
538 {
539 struct mpd_status *status;
541 if (MPD_ERROR(c))
542 return -1;
544 if (mpd_connection_cmp_server_version(c->connection, 0, 16, 0) < 0)
545 return mpdclient_cmd_delete_range_fallback(c, start, end);
547 /* MPD 0.16 supports "delete" with a range argument */
549 /* send the delete command to mpd; at the same time, get the
550 new status (to verify the playlist id) */
552 if (!mpd_command_list_begin(c->connection, false) ||
553 !mpd_send_delete_range(c->connection, start, end) ||
554 !mpd_send_status(c->connection) ||
555 !mpd_command_list_end(c->connection))
556 return mpdclient_handle_error(c);
558 c->events |= MPD_IDLE_PLAYLIST;
560 status = mpd_recv_status(c->connection);
561 if (status != NULL) {
562 if (c->status != NULL)
563 mpd_status_free(c->status);
564 c->status = status;
565 }
567 if (!mpd_response_finish(c->connection))
568 return mpdclient_handle_error(c);
570 if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - (end - start) &&
571 mpd_status_get_queue_version(status) == c->playlist.version + 1) {
572 /* the cheap route: match on the new playlist length
573 and its version, we can keep our local playlist
574 copy in sync */
575 c->playlist.version = mpd_status_get_queue_version(status);
577 /* remove the song from the local playlist */
578 while (end > start) {
579 --end;
581 /* remove references to the song */
582 if (c->song == playlist_get(&c->playlist, end))
583 c->song = NULL;
585 playlist_remove(&c->playlist, end);
586 }
587 }
589 return 0;
590 }
592 gint
593 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
594 {
595 gint n;
596 struct mpd_song *song1, *song2;
598 if (MPD_ERROR(c))
599 return -1;
601 if (old_index == new_index || new_index < 0 ||
602 (guint)new_index >= c->playlist.list->len)
603 return -1;
605 song1 = playlist_get(&c->playlist, old_index);
606 song2 = playlist_get(&c->playlist, new_index);
608 /* send the move command to mpd */
609 mpd_send_swap_id(c->connection,
610 mpd_song_get_id(song1), mpd_song_get_id(song2));
611 if( (n=mpdclient_finish_command(c)) )
612 return n;
614 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
615 /* update the playlist */
616 playlist_swap(&c->playlist, old_index, new_index);
618 /* increment the playlist id, so we don't retrieve a new playlist */
619 c->playlist.version++;
620 #endif
622 c->events |= MPD_IDLE_PLAYLIST;
624 return 0;
625 }
627 gint
628 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
629 {
630 gint retval = 0;
632 if (MPD_ERROR(c))
633 return -1;
635 mpd_send_save(c->connection, filename_utf8);
636 if ((retval = mpdclient_finish_command(c)) == 0) {
637 c->events |= MPD_IDLE_STORED_PLAYLIST;
638 }
640 return retval;
641 }
643 gint
644 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
645 {
646 if (MPD_ERROR(c))
647 return -1;
649 mpd_send_load(c->connection, filename_utf8);
650 return mpdclient_finish_command(c);
651 }
653 gint
654 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
655 {
656 gint retval = 0;
658 if (MPD_ERROR(c))
659 return -1;
661 mpd_send_rm(c->connection, filename_utf8);
662 if ((retval = mpdclient_finish_command(c)) == 0)
663 c->events |= MPD_IDLE_STORED_PLAYLIST;
665 return retval;
666 }
669 /****************************************************************************/
670 /*** Playlist management functions ******************************************/
671 /****************************************************************************/
673 /* update playlist */
674 bool
675 mpdclient_playlist_update(struct mpdclient *c)
676 {
677 struct mpd_entity *entity;
679 if (MPD_ERROR(c))
680 return false;
682 playlist_clear(&c->playlist);
684 mpd_send_list_queue_meta(c->connection);
685 while ((entity = mpd_recv_entity(c->connection))) {
686 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
687 playlist_append(&c->playlist, mpd_entity_get_song(entity));
689 mpd_entity_free(entity);
690 }
692 c->playlist.version = mpd_status_get_queue_version(c->status);
693 c->song = NULL;
695 return mpdclient_finish_command(c) == 0;
696 }
698 /* update playlist (plchanges) */
699 bool
700 mpdclient_playlist_update_changes(struct mpdclient *c)
701 {
702 struct mpd_song *song;
703 guint length;
705 if (MPD_ERROR(c))
706 return false;
708 mpd_send_queue_changes_meta(c->connection, c->playlist.version);
710 while ((song = mpd_recv_song(c->connection)) != NULL) {
711 int pos = mpd_song_get_pos(song);
713 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
714 /* update song */
715 playlist_replace(&c->playlist, pos, song);
716 } else {
717 /* add a new song */
718 playlist_append(&c->playlist, song);
719 }
721 mpd_song_free(song);
722 }
724 /* remove trailing songs */
726 length = mpd_status_get_queue_length(c->status);
727 while (length < c->playlist.list->len) {
728 guint pos = c->playlist.list->len - 1;
730 /* Remove the last playlist entry */
731 playlist_remove(&c->playlist, pos);
732 }
734 c->song = NULL;
735 c->playlist.version = mpd_status_get_queue_version(c->status);
737 return mpdclient_finish_command(c) == 0;
738 }
741 /****************************************************************************/
742 /*** Filelist functions *****************************************************/
743 /****************************************************************************/
745 struct filelist *
746 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
747 {
748 struct filelist *filelist;
749 struct mpd_entity *entity;
751 if (MPD_ERROR(c))
752 return NULL;
754 mpd_send_list_meta(c->connection, path);
755 filelist = filelist_new();
757 while ((entity = mpd_recv_entity(c->connection)) != NULL)
758 filelist_append(filelist, entity);
760 if (mpdclient_finish_command(c)) {
761 filelist_free(filelist);
762 return NULL;
763 }
765 filelist_sort_dir_play(filelist, compare_filelistentry);
767 return filelist;
768 }
770 static struct filelist *
771 mpdclient_recv_filelist_response(struct mpdclient *c)
772 {
773 struct filelist *filelist;
774 struct mpd_entity *entity;
776 filelist = filelist_new();
778 while ((entity = mpd_recv_entity(c->connection)) != NULL)
779 filelist_append(filelist, entity);
781 if (mpdclient_finish_command(c)) {
782 filelist_free(filelist);
783 return NULL;
784 }
786 return filelist;
787 }
789 struct filelist *
790 mpdclient_filelist_search(struct mpdclient *c,
791 int exact_match,
792 enum mpd_tag_type tag,
793 gchar *filter_utf8)
794 {
795 if (MPD_ERROR(c))
796 return NULL;
798 mpd_search_db_songs(c->connection, exact_match);
799 mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
800 tag, filter_utf8);
801 mpd_search_commit(c->connection);
803 return mpdclient_recv_filelist_response(c);
804 }
806 int
807 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
808 {
809 guint i;
811 if (MPD_ERROR(c))
812 return -1;
814 if (filelist_is_empty(fl))
815 return 0;
817 mpd_command_list_begin(c->connection, false);
819 for (i = 0; i < filelist_length(fl); ++i) {
820 struct filelist_entry *entry = filelist_get(fl, i);
821 struct mpd_entity *entity = entry->entity;
823 if (entity != NULL &&
824 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
825 const struct mpd_song *song =
826 mpd_entity_get_song(entity);
827 const char *uri = mpd_song_get_uri(song);
829 if (uri != NULL)
830 mpd_send_add(c->connection, uri);
831 }
832 }
834 mpd_command_list_end(c->connection);
835 return mpdclient_finish_command(c);
836 }
838 GList *
839 mpdclient_get_artists(struct mpdclient *c)
840 {
841 GList *list = NULL;
842 struct mpd_pair *pair;
844 if (MPD_ERROR(c))
845 return NULL;
847 mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
848 mpd_search_commit(c->connection);
850 while ((pair = mpd_recv_pair_tag(c->connection,
851 MPD_TAG_ARTIST)) != NULL) {
852 list = g_list_append(list, g_strdup(pair->value));
853 mpd_return_pair(c->connection, pair);
854 }
856 if (mpdclient_finish_command(c))
857 return string_list_free(list);
859 return list;
860 }
862 GList *
863 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
864 {
865 GList *list = NULL;
866 struct mpd_pair *pair;
868 if (MPD_ERROR(c))
869 return NULL;
871 mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
872 if (artist_utf8 != NULL)
873 mpd_search_add_tag_constraint(c->connection,
874 MPD_OPERATOR_DEFAULT,
875 MPD_TAG_ARTIST, artist_utf8);
876 mpd_search_commit(c->connection);
878 while ((pair = mpd_recv_pair_tag(c->connection,
879 MPD_TAG_ALBUM)) != NULL) {
880 list = g_list_append(list, g_strdup(pair->value));
881 mpd_return_pair(c->connection, pair);
882 }
884 if (mpdclient_finish_command(c))
885 return string_list_free(list);
887 return list;
888 }