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)
108 {
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;
131 }
133 static gint
134 mpdclient_finish_command(struct mpdclient *c)
135 {
136 return mpd_response_finish(c->connection)
137 ? 0 : mpdclient_handle_error(c);
138 }
140 struct mpdclient *
141 mpdclient_new(void)
142 {
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;
150 }
152 void
153 mpdclient_free(struct mpdclient *c)
154 {
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);
163 }
165 void
166 mpdclient_disconnect(struct mpdclient *c)
167 {
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;
180 }
182 gint
183 mpdclient_connect(struct mpdclient *c,
184 const gchar *host,
185 gint port,
186 gfloat _timeout,
187 const gchar *password)
188 {
189 gint retval = 0;
191 /* close any open connection */
192 if( c->connection )
193 mpdclient_disconnect(c);
195 /* connect to MPD */
196 c->connection = mpd_connection_new(host, port, _timeout * 1000);
197 if (c->connection == NULL)
198 g_error("Out of memory");
200 if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
201 retval = mpdclient_handle_error(c);
202 if (retval != 0) {
203 mpd_connection_free(c->connection);
204 c->connection = NULL;
205 }
207 return retval;
208 }
210 /* send password */
211 if( password ) {
212 mpd_send_password(c->connection, password);
213 retval = mpdclient_finish_command(c);
214 }
216 return retval;
217 }
219 gint
220 mpdclient_update(struct mpdclient *c)
221 {
222 gint retval = 0;
224 c->volume = MPD_STATUS_NO_VOLUME;
226 if (MPD_ERROR(c))
227 return -1;
229 /* free the old status */
230 if (c->status)
231 mpd_status_free(c->status);
233 /* retrieve new status */
234 c->status = mpd_run_status(c->connection);
235 if (c->status == NULL)
236 return mpdclient_handle_error(c);
238 if (c->updatingdb &&
239 c->updatingdb != mpd_status_get_update_id(c->status))
240 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
242 c->updatingdb = mpd_status_get_update_id(c->status);
243 c->volume = mpd_status_get_volume(c->status);
245 /* check if the playlist needs an update */
246 if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
247 if (!playlist_is_empty(&c->playlist))
248 retval = mpdclient_playlist_update_changes(c);
249 else
250 retval = mpdclient_playlist_update(c);
251 }
253 /* update the current song */
254 if (!c->song || mpd_status_get_song_id(c->status)) {
255 c->song = playlist_get_song(&c->playlist,
256 mpd_status_get_song_pos(c->status));
257 }
259 return retval;
260 }
263 /****************************************************************************/
264 /*** MPD Commands **********************************************************/
265 /****************************************************************************/
267 gint
268 mpdclient_cmd_play(struct mpdclient *c, gint idx)
269 {
270 const struct mpd_song *song = playlist_get_song(&c->playlist, idx);
272 if (MPD_ERROR(c))
273 return -1;
275 if (song)
276 mpd_send_play_id(c->connection, mpd_song_get_id(song));
277 else
278 mpd_send_play(c->connection);
280 return mpdclient_finish_command(c);
281 }
283 gint
284 mpdclient_cmd_pause(struct mpdclient *c, gint value)
285 {
286 if (MPD_ERROR(c))
287 return -1;
289 mpd_send_pause(c->connection, value);
290 return mpdclient_finish_command(c);
291 }
293 gint
294 mpdclient_cmd_crop(struct mpdclient *c)
295 {
296 struct mpd_status *status;
297 bool playing;
298 int length, current;
300 if (MPD_ERROR(c))
301 return -1;
303 status = mpd_run_status(c->connection);
304 if (status == NULL)
305 return mpdclient_handle_error(c);
307 playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
308 mpd_status_get_state(status) == MPD_STATE_PAUSE;
309 length = mpd_status_get_queue_length(status);
310 current = mpd_status_get_song_pos(status);
312 mpd_status_free(status);
314 if (!playing || length < 2)
315 return 0;
317 mpd_command_list_begin(c->connection, false);
319 while (--length >= 0)
320 if (length != current)
321 mpd_send_delete(c->connection, length);
323 mpd_command_list_end(c->connection);
325 return mpdclient_finish_command(c);
326 }
328 gint
329 mpdclient_cmd_stop(struct mpdclient *c)
330 {
331 if (MPD_ERROR(c))
332 return -1;
334 mpd_send_stop(c->connection);
335 return mpdclient_finish_command(c);
336 }
338 gint
339 mpdclient_cmd_next(struct mpdclient *c)
340 {
341 if (MPD_ERROR(c))
342 return -1;
344 mpd_send_next(c->connection);
345 return mpdclient_finish_command(c);
346 }
348 gint
349 mpdclient_cmd_prev(struct mpdclient *c)
350 {
351 if (MPD_ERROR(c))
352 return -1;
354 mpd_send_previous(c->connection);
355 return mpdclient_finish_command(c);
356 }
358 gint
359 mpdclient_cmd_seek(struct mpdclient *c, gint id, gint pos)
360 {
361 if (MPD_ERROR(c))
362 return -1;
364 mpd_send_seek_id(c->connection, id, pos);
365 return mpdclient_finish_command(c);
366 }
368 gint
369 mpdclient_cmd_shuffle(struct mpdclient *c)
370 {
371 if (MPD_ERROR(c))
372 return -1;
374 mpd_send_shuffle(c->connection);
375 return mpdclient_finish_command(c);
376 }
378 gint
379 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
380 {
381 mpd_send_shuffle_range(c->connection, start, end);
382 return mpdclient_finish_command(c);
383 }
385 gint
386 mpdclient_cmd_clear(struct mpdclient *c)
387 {
388 gint retval = 0;
390 if (MPD_ERROR(c))
391 return -1;
393 mpd_send_clear(c->connection);
394 retval = mpdclient_finish_command(c);
395 /* call playlist updated callback */
396 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
397 return retval;
398 }
400 gint
401 mpdclient_cmd_repeat(struct mpdclient *c, gint value)
402 {
403 if (MPD_ERROR(c))
404 return -1;
406 mpd_send_repeat(c->connection, value);
407 return mpdclient_finish_command(c);
408 }
410 gint
411 mpdclient_cmd_random(struct mpdclient *c, gint value)
412 {
413 if (MPD_ERROR(c))
414 return -1;
416 mpd_send_random(c->connection, value);
417 return mpdclient_finish_command(c);
418 }
420 gint
421 mpdclient_cmd_single(struct mpdclient *c, gint value)
422 {
423 if (MPD_ERROR(c))
424 return -1;
426 mpd_send_single(c->connection, value);
427 return mpdclient_finish_command(c);
428 }
430 gint
431 mpdclient_cmd_consume(struct mpdclient *c, gint value)
432 {
433 if (MPD_ERROR(c))
434 return -1;
436 mpd_send_consume(c->connection, value);
437 return mpdclient_finish_command(c);
438 }
440 gint
441 mpdclient_cmd_crossfade(struct mpdclient *c, gint value)
442 {
443 if (MPD_ERROR(c))
444 return -1;
446 mpd_send_crossfade(c->connection, value);
447 return mpdclient_finish_command(c);
448 }
450 gint
451 mpdclient_cmd_volume(struct mpdclient *c, gint value)
452 {
453 if (MPD_ERROR(c))
454 return -1;
456 mpd_send_set_volume(c->connection, value);
457 return mpdclient_finish_command(c);
458 }
460 gint mpdclient_cmd_volume_up(struct mpdclient *c)
461 {
462 if (MPD_ERROR(c))
463 return -1;
465 if (c->status == NULL ||
466 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
467 return 0;
469 if (c->volume == MPD_STATUS_NO_VOLUME)
470 c->volume = mpd_status_get_volume(c->status);
472 if (c->volume >= 100)
473 return 0;
475 return mpdclient_cmd_volume(c, ++c->volume);
476 }
478 gint mpdclient_cmd_volume_down(struct mpdclient *c)
479 {
480 if (MPD_ERROR(c))
481 return -1;
483 if (c->status == NULL ||
484 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
485 return 0;
487 if (c->volume == MPD_STATUS_NO_VOLUME)
488 c->volume = mpd_status_get_volume(c->status);
490 if (c->volume <= 0)
491 return 0;
493 return mpdclient_cmd_volume(c, --c->volume);
494 }
496 gint
497 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
498 {
499 if (MPD_ERROR(c))
500 return -1;
502 mpd_send_add(c->connection, path_utf8);
503 return mpdclient_finish_command(c);
504 }
506 gint
507 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
508 {
509 gint retval = 0;
511 if (MPD_ERROR(c))
512 return -1;
514 if (song == NULL)
515 return -1;
517 /* send the add command to mpd */
518 mpd_send_add(c->connection, mpd_song_get_uri(song));
519 if( (retval=mpdclient_finish_command(c)) )
520 return retval;
522 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
523 /* add the song to playlist */
524 playlist_append(&c->playlist, song);
526 /* increment the playlist id, so we don't retrieve a new playlist */
527 c->playlist.id++;
529 /* call playlist updated callback */
530 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
531 #endif
533 return 0;
534 }
536 gint
537 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
538 {
539 gint retval = 0;
540 struct mpd_song *song;
542 if (MPD_ERROR(c))
543 return -1;
545 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
546 return -1;
548 song = playlist_get(&c->playlist, idx);
550 /* send the delete command to mpd */
551 mpd_send_delete_id(c->connection, mpd_song_get_id(song));
552 if( (retval=mpdclient_finish_command(c)) )
553 return retval;
555 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
556 /* increment the playlist id, so we don't retrieve a new playlist */
557 c->playlist.id++;
559 /* remove the song from the playlist */
560 playlist_remove_reuse(&c->playlist, idx);
562 /* call playlist updated callback */
563 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
565 /* remove references to the song */
566 if (c->song == song)
567 c->song = NULL;
569 mpd_song_free(song);
570 #endif
572 return 0;
573 }
575 gint
576 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
577 {
578 gint n;
579 struct mpd_song *song1, *song2;
581 if (MPD_ERROR(c))
582 return -1;
584 if (old_index == new_index || new_index < 0 ||
585 (guint)new_index >= c->playlist.list->len)
586 return -1;
588 song1 = playlist_get(&c->playlist, old_index);
589 song2 = playlist_get(&c->playlist, new_index);
591 /* send the move command to mpd */
592 mpd_send_swap_id(c->connection,
593 mpd_song_get_id(song1), mpd_song_get_id(song2));
594 if( (n=mpdclient_finish_command(c)) )
595 return n;
597 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
598 /* update the playlist */
599 playlist_swap(&c->playlist, old_index, new_index);
601 /* increment the playlist id, so we don't retrieve a new playlist */
602 c->playlist.id++;
603 #endif
605 /* call playlist updated callback */
606 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
608 return 0;
609 }
611 gint
612 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
613 {
614 gint retval = 0;
616 if (MPD_ERROR(c))
617 return -1;
619 mpd_send_save(c->connection, filename_utf8);
620 if ((retval = mpdclient_finish_command(c)) == 0)
621 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
622 return retval;
623 }
625 gint
626 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
627 {
628 if (MPD_ERROR(c))
629 return -1;
631 mpd_send_load(c->connection, filename_utf8);
632 return mpdclient_finish_command(c);
633 }
635 gint
636 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
637 {
638 gint retval = 0;
640 if (MPD_ERROR(c))
641 return -1;
643 mpd_send_rm(c->connection, filename_utf8);
644 if ((retval = mpdclient_finish_command(c)) == 0)
645 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
646 return retval;
647 }
650 /****************************************************************************/
651 /*** Callback management functions ******************************************/
652 /****************************************************************************/
654 static void
655 do_list_callbacks(struct mpdclient *c, GList *list, gint event, gpointer data)
656 {
657 while (list) {
658 mpdc_list_cb_t fn = list->data;
660 fn(c, event, data);
661 list = list->next;
662 }
663 }
665 void
666 mpdclient_playlist_callback(struct mpdclient *c, int event, gpointer data)
667 {
668 do_list_callbacks(c, c->playlist_callbacks, event, data);
669 }
671 void
672 mpdclient_install_playlist_callback(struct mpdclient *c,mpdc_list_cb_t cb)
673 {
674 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
675 }
677 void
678 mpdclient_remove_playlist_callback(struct mpdclient *c, mpdc_list_cb_t cb)
679 {
680 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
681 }
683 void
684 mpdclient_browse_callback(struct mpdclient *c, int event, gpointer data)
685 {
686 do_list_callbacks(c, c->browse_callbacks, event, data);
687 }
690 void
691 mpdclient_install_browse_callback(struct mpdclient *c,mpdc_list_cb_t cb)
692 {
693 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
694 }
696 void
697 mpdclient_remove_browse_callback(struct mpdclient *c, mpdc_list_cb_t cb)
698 {
699 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
700 }
702 void
703 mpdclient_install_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
704 {
705 c->error_callbacks = g_list_append(c->error_callbacks, cb);
706 }
708 void
709 mpdclient_remove_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
710 {
711 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
712 }
715 /****************************************************************************/
716 /*** Playlist management functions ******************************************/
717 /****************************************************************************/
719 /* update playlist */
720 gint
721 mpdclient_playlist_update(struct mpdclient *c)
722 {
723 struct mpd_entity *entity;
725 if (MPD_ERROR(c))
726 return -1;
728 playlist_clear(&c->playlist);
730 mpd_send_list_queue_meta(c->connection);
731 while ((entity = mpd_recv_entity(c->connection))) {
732 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
733 playlist_append(&c->playlist, mpd_entity_get_song(entity));
735 mpd_entity_free(entity);
736 }
738 c->playlist.id = mpd_status_get_queue_version(c->status);
739 c->song = NULL;
741 /* call playlist updated callbacks */
742 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
744 return mpdclient_finish_command(c);
745 }
747 /* update playlist (plchanges) */
748 gint
749 mpdclient_playlist_update_changes(struct mpdclient *c)
750 {
751 struct mpd_song *song;
752 guint length;
754 if (MPD_ERROR(c))
755 return -1;
757 mpd_send_queue_changes_meta(c->connection, c->playlist.id);
759 while ((song = mpd_recv_song(c->connection)) != NULL) {
760 int pos = mpd_song_get_pos(song);
762 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
763 /* update song */
764 playlist_replace(&c->playlist, pos, song);
765 } else {
766 /* add a new song */
767 playlist_append(&c->playlist, song);
768 }
770 mpd_song_free(song);
771 }
773 /* remove trailing songs */
775 length = mpd_status_get_queue_length(c->status);
776 while (length < c->playlist.list->len) {
777 guint pos = c->playlist.list->len - 1;
779 /* Remove the last playlist entry */
780 playlist_remove(&c->playlist, pos);
781 }
783 c->song = NULL;
784 c->playlist.id = mpd_status_get_queue_version(c->status);
786 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
788 return 0;
789 }
792 /****************************************************************************/
793 /*** Filelist functions *****************************************************/
794 /****************************************************************************/
796 struct filelist *
797 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
798 {
799 struct filelist *filelist;
800 struct mpd_entity *entity;
802 if (MPD_ERROR(c))
803 return NULL;
805 mpd_send_list_meta(c->connection, path);
806 filelist = filelist_new();
807 if (path && path[0] && strcmp(path, "/"))
808 /* add a dummy entry for ./.. */
809 filelist_append(filelist, NULL);
811 while ((entity = mpd_recv_entity(c->connection)) != NULL)
812 filelist_append(filelist, entity);
814 /* If there's an error, ignore it. We'll return an empty filelist. */
815 mpdclient_finish_command(c);
817 filelist_sort_dir_play(filelist, compare_filelistentry);
819 return filelist;
820 }
822 static struct filelist *
823 mpdclient_recv_filelist_response(struct mpdclient *c)
824 {
825 struct filelist *filelist;
826 struct mpd_entity *entity;
828 filelist = filelist_new();
830 while ((entity = mpd_recv_entity(c->connection)) != NULL)
831 filelist_append(filelist, entity);
833 if (mpdclient_finish_command(c)) {
834 filelist_free(filelist);
835 return NULL;
836 }
838 return filelist;
839 }
841 struct filelist *
842 mpdclient_filelist_search(struct mpdclient *c,
843 int exact_match,
844 enum mpd_tag_type tag,
845 gchar *filter_utf8)
846 {
847 if (MPD_ERROR(c))
848 return NULL;
850 mpd_search_db_songs(c->connection, exact_match);
851 mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
852 tag, filter_utf8);
853 mpd_search_commit(c->connection);
855 return mpdclient_recv_filelist_response(c);
856 }
858 int
859 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
860 {
861 guint i;
863 if (MPD_ERROR(c))
864 return -1;
866 if (filelist_is_empty(fl))
867 return 0;
869 mpd_command_list_begin(c->connection, false);
871 for (i = 0; i < filelist_length(fl); ++i) {
872 struct filelist_entry *entry = filelist_get(fl, i);
873 struct mpd_entity *entity = entry->entity;
875 if (entity != NULL &&
876 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
877 const struct mpd_song *song =
878 mpd_entity_get_song(entity);
879 const char *uri = mpd_song_get_uri(song);
881 if (uri != NULL)
882 mpd_send_add(c->connection, uri);
883 }
884 }
886 mpd_command_list_end(c->connection);
887 return mpdclient_finish_command(c);
888 }
890 GList *
891 mpdclient_get_artists(struct mpdclient *c)
892 {
893 GList *list = NULL;
894 struct mpd_pair *pair;
896 if (MPD_ERROR(c))
897 return NULL;
899 mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
900 mpd_search_commit(c->connection);
902 while ((pair = mpd_recv_pair_tag(c->connection,
903 MPD_TAG_ARTIST)) != NULL) {
904 list = g_list_append(list, g_strdup(pair->value));
905 mpd_return_pair(c->connection, pair);
906 }
908 if (mpdclient_finish_command(c))
909 return string_list_free(list);
911 return list;
912 }
914 GList *
915 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
916 {
917 GList *list = NULL;
918 struct mpd_pair *pair;
920 if (MPD_ERROR(c))
921 return NULL;
923 mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
924 if (artist_utf8 != NULL)
925 mpd_search_add_tag_constraint(c->connection,
926 MPD_OPERATOR_DEFAULT,
927 MPD_TAG_ARTIST, artist_utf8);
928 mpd_search_commit(c->connection);
930 while ((pair = mpd_recv_pair_tag(c->connection,
931 MPD_TAG_ALBUM)) != NULL) {
932 list = g_list_append(list, g_strdup(pair->value));
933 mpd_return_pair(c->connection, pair);
934 }
936 if (mpdclient_finish_command(c))
937 return string_list_free(list);
939 return list;
940 }