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 bool
183 mpdclient_connect(struct mpdclient *c,
184 const gchar *host,
185 gint port,
186 gfloat _timeout,
187 const gchar *password)
188 {
189 /* close any open connection */
190 if( c->connection )
191 mpdclient_disconnect(c);
193 /* connect to MPD */
194 c->connection = mpd_connection_new(host, port, _timeout * 1000);
195 if (c->connection == NULL)
196 g_error("Out of memory");
198 if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
199 mpdclient_handle_error(c);
200 mpdclient_disconnect(c);
201 return false;
202 }
204 /* send password */
205 if (password != NULL && !mpd_run_password(c->connection, password)) {
206 mpdclient_handle_error(c);
207 mpdclient_disconnect(c);
208 return false;
209 }
211 return true;
212 }
214 bool
215 mpdclient_update(struct mpdclient *c)
216 {
217 bool retval;
219 c->volume = MPD_STATUS_NO_VOLUME;
221 if (MPD_ERROR(c))
222 return false;
224 /* free the old status */
225 if (c->status)
226 mpd_status_free(c->status);
228 /* retrieve new status */
229 c->status = mpd_run_status(c->connection);
230 if (c->status == NULL)
231 return mpdclient_handle_error(c) == 0;
233 if (c->updatingdb &&
234 c->updatingdb != mpd_status_get_update_id(c->status))
235 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
237 c->updatingdb = mpd_status_get_update_id(c->status);
238 c->volume = mpd_status_get_volume(c->status);
240 /* check if the playlist needs an update */
241 if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
242 if (!playlist_is_empty(&c->playlist))
243 retval = mpdclient_playlist_update_changes(c);
244 else
245 retval = mpdclient_playlist_update(c);
246 }
248 /* update the current song */
249 if (!c->song || mpd_status_get_song_id(c->status)) {
250 c->song = playlist_get_song(&c->playlist,
251 mpd_status_get_song_pos(c->status));
252 }
254 return retval;
255 }
258 /****************************************************************************/
259 /*** MPD Commands **********************************************************/
260 /****************************************************************************/
262 gint
263 mpdclient_cmd_play(struct mpdclient *c, gint idx)
264 {
265 const struct mpd_song *song = playlist_get_song(&c->playlist, idx);
267 if (MPD_ERROR(c))
268 return -1;
270 if (song)
271 mpd_send_play_id(c->connection, mpd_song_get_id(song));
272 else
273 mpd_send_play(c->connection);
275 return mpdclient_finish_command(c);
276 }
278 gint
279 mpdclient_cmd_pause(struct mpdclient *c, gint value)
280 {
281 if (MPD_ERROR(c))
282 return -1;
284 mpd_send_pause(c->connection, value);
285 return mpdclient_finish_command(c);
286 }
288 gint
289 mpdclient_cmd_crop(struct mpdclient *c)
290 {
291 struct mpd_status *status;
292 bool playing;
293 int length, current;
295 if (MPD_ERROR(c))
296 return -1;
298 status = mpd_run_status(c->connection);
299 if (status == NULL)
300 return mpdclient_handle_error(c);
302 playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
303 mpd_status_get_state(status) == MPD_STATE_PAUSE;
304 length = mpd_status_get_queue_length(status);
305 current = mpd_status_get_song_pos(status);
307 mpd_status_free(status);
309 if (!playing || length < 2)
310 return 0;
312 mpd_command_list_begin(c->connection, false);
314 while (--length >= 0)
315 if (length != current)
316 mpd_send_delete(c->connection, length);
318 mpd_command_list_end(c->connection);
320 return mpdclient_finish_command(c);
321 }
323 gint
324 mpdclient_cmd_stop(struct mpdclient *c)
325 {
326 if (MPD_ERROR(c))
327 return -1;
329 mpd_send_stop(c->connection);
330 return mpdclient_finish_command(c);
331 }
333 gint
334 mpdclient_cmd_next(struct mpdclient *c)
335 {
336 if (MPD_ERROR(c))
337 return -1;
339 mpd_send_next(c->connection);
340 return mpdclient_finish_command(c);
341 }
343 gint
344 mpdclient_cmd_prev(struct mpdclient *c)
345 {
346 if (MPD_ERROR(c))
347 return -1;
349 mpd_send_previous(c->connection);
350 return mpdclient_finish_command(c);
351 }
353 gint
354 mpdclient_cmd_seek(struct mpdclient *c, gint id, gint pos)
355 {
356 if (MPD_ERROR(c))
357 return -1;
359 mpd_send_seek_id(c->connection, id, pos);
360 return mpdclient_finish_command(c);
361 }
363 gint
364 mpdclient_cmd_shuffle(struct mpdclient *c)
365 {
366 if (MPD_ERROR(c))
367 return -1;
369 mpd_send_shuffle(c->connection);
370 return mpdclient_finish_command(c);
371 }
373 gint
374 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
375 {
376 mpd_send_shuffle_range(c->connection, start, end);
377 return mpdclient_finish_command(c);
378 }
380 gint
381 mpdclient_cmd_clear(struct mpdclient *c)
382 {
383 gint retval = 0;
385 if (MPD_ERROR(c))
386 return -1;
388 mpd_send_clear(c->connection);
389 retval = mpdclient_finish_command(c);
390 /* call playlist updated callback */
391 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
392 return retval;
393 }
395 gint
396 mpdclient_cmd_repeat(struct mpdclient *c, gint value)
397 {
398 if (MPD_ERROR(c))
399 return -1;
401 mpd_send_repeat(c->connection, value);
402 return mpdclient_finish_command(c);
403 }
405 gint
406 mpdclient_cmd_random(struct mpdclient *c, gint value)
407 {
408 if (MPD_ERROR(c))
409 return -1;
411 mpd_send_random(c->connection, value);
412 return mpdclient_finish_command(c);
413 }
415 gint
416 mpdclient_cmd_single(struct mpdclient *c, gint value)
417 {
418 if (MPD_ERROR(c))
419 return -1;
421 mpd_send_single(c->connection, value);
422 return mpdclient_finish_command(c);
423 }
425 gint
426 mpdclient_cmd_consume(struct mpdclient *c, gint value)
427 {
428 if (MPD_ERROR(c))
429 return -1;
431 mpd_send_consume(c->connection, value);
432 return mpdclient_finish_command(c);
433 }
435 gint
436 mpdclient_cmd_crossfade(struct mpdclient *c, gint value)
437 {
438 if (MPD_ERROR(c))
439 return -1;
441 mpd_send_crossfade(c->connection, value);
442 return mpdclient_finish_command(c);
443 }
445 gint
446 mpdclient_cmd_volume(struct mpdclient *c, gint value)
447 {
448 if (MPD_ERROR(c))
449 return -1;
451 mpd_send_set_volume(c->connection, value);
452 return mpdclient_finish_command(c);
453 }
455 gint mpdclient_cmd_volume_up(struct mpdclient *c)
456 {
457 if (MPD_ERROR(c))
458 return -1;
460 if (c->status == NULL ||
461 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
462 return 0;
464 if (c->volume == MPD_STATUS_NO_VOLUME)
465 c->volume = mpd_status_get_volume(c->status);
467 if (c->volume >= 100)
468 return 0;
470 return mpdclient_cmd_volume(c, ++c->volume);
471 }
473 gint mpdclient_cmd_volume_down(struct mpdclient *c)
474 {
475 if (MPD_ERROR(c))
476 return -1;
478 if (c->status == NULL ||
479 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
480 return 0;
482 if (c->volume == MPD_STATUS_NO_VOLUME)
483 c->volume = mpd_status_get_volume(c->status);
485 if (c->volume <= 0)
486 return 0;
488 return mpdclient_cmd_volume(c, --c->volume);
489 }
491 gint
492 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
493 {
494 if (MPD_ERROR(c))
495 return -1;
497 mpd_send_add(c->connection, path_utf8);
498 return mpdclient_finish_command(c);
499 }
501 gint
502 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
503 {
504 gint retval = 0;
506 if (MPD_ERROR(c))
507 return -1;
509 if (song == NULL)
510 return -1;
512 /* send the add command to mpd */
513 mpd_send_add(c->connection, mpd_song_get_uri(song));
514 if( (retval=mpdclient_finish_command(c)) )
515 return retval;
517 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
518 /* add the song to playlist */
519 playlist_append(&c->playlist, song);
521 /* increment the playlist id, so we don't retrieve a new playlist */
522 c->playlist.id++;
524 /* call playlist updated callback */
525 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
526 #endif
528 return 0;
529 }
531 gint
532 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
533 {
534 gint retval = 0;
535 struct mpd_song *song;
537 if (MPD_ERROR(c))
538 return -1;
540 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
541 return -1;
543 song = playlist_get(&c->playlist, idx);
545 /* send the delete command to mpd */
546 mpd_send_delete_id(c->connection, mpd_song_get_id(song));
547 if( (retval=mpdclient_finish_command(c)) )
548 return retval;
550 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
551 /* increment the playlist id, so we don't retrieve a new playlist */
552 c->playlist.id++;
554 /* remove the song from the playlist */
555 playlist_remove_reuse(&c->playlist, idx);
557 /* call playlist updated callback */
558 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
560 /* remove references to the song */
561 if (c->song == song)
562 c->song = NULL;
564 mpd_song_free(song);
565 #endif
567 return 0;
568 }
570 gint
571 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
572 {
573 gint n;
574 struct mpd_song *song1, *song2;
576 if (MPD_ERROR(c))
577 return -1;
579 if (old_index == new_index || new_index < 0 ||
580 (guint)new_index >= c->playlist.list->len)
581 return -1;
583 song1 = playlist_get(&c->playlist, old_index);
584 song2 = playlist_get(&c->playlist, new_index);
586 /* send the move command to mpd */
587 mpd_send_swap_id(c->connection,
588 mpd_song_get_id(song1), mpd_song_get_id(song2));
589 if( (n=mpdclient_finish_command(c)) )
590 return n;
592 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
593 /* update the playlist */
594 playlist_swap(&c->playlist, old_index, new_index);
596 /* increment the playlist id, so we don't retrieve a new playlist */
597 c->playlist.id++;
598 #endif
600 /* call playlist updated callback */
601 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
603 return 0;
604 }
606 gint
607 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
608 {
609 gint retval = 0;
611 if (MPD_ERROR(c))
612 return -1;
614 mpd_send_save(c->connection, filename_utf8);
615 if ((retval = mpdclient_finish_command(c)) == 0)
616 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
617 return retval;
618 }
620 gint
621 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
622 {
623 if (MPD_ERROR(c))
624 return -1;
626 mpd_send_load(c->connection, filename_utf8);
627 return mpdclient_finish_command(c);
628 }
630 gint
631 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
632 {
633 gint retval = 0;
635 if (MPD_ERROR(c))
636 return -1;
638 mpd_send_rm(c->connection, filename_utf8);
639 if ((retval = mpdclient_finish_command(c)) == 0)
640 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
641 return retval;
642 }
645 /****************************************************************************/
646 /*** Callback management functions ******************************************/
647 /****************************************************************************/
649 static void
650 do_list_callbacks(struct mpdclient *c, GList *list, gint event, gpointer data)
651 {
652 while (list) {
653 mpdc_list_cb_t fn = list->data;
655 fn(c, event, data);
656 list = list->next;
657 }
658 }
660 void
661 mpdclient_playlist_callback(struct mpdclient *c, int event, gpointer data)
662 {
663 do_list_callbacks(c, c->playlist_callbacks, event, data);
664 }
666 void
667 mpdclient_install_playlist_callback(struct mpdclient *c,mpdc_list_cb_t cb)
668 {
669 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
670 }
672 void
673 mpdclient_remove_playlist_callback(struct mpdclient *c, mpdc_list_cb_t cb)
674 {
675 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
676 }
678 void
679 mpdclient_browse_callback(struct mpdclient *c, int event, gpointer data)
680 {
681 do_list_callbacks(c, c->browse_callbacks, event, data);
682 }
685 void
686 mpdclient_install_browse_callback(struct mpdclient *c,mpdc_list_cb_t cb)
687 {
688 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
689 }
691 void
692 mpdclient_remove_browse_callback(struct mpdclient *c, mpdc_list_cb_t cb)
693 {
694 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
695 }
697 void
698 mpdclient_install_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
699 {
700 c->error_callbacks = g_list_append(c->error_callbacks, cb);
701 }
703 void
704 mpdclient_remove_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
705 {
706 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
707 }
710 /****************************************************************************/
711 /*** Playlist management functions ******************************************/
712 /****************************************************************************/
714 /* update playlist */
715 bool
716 mpdclient_playlist_update(struct mpdclient *c)
717 {
718 struct mpd_entity *entity;
720 if (MPD_ERROR(c))
721 return false;
723 playlist_clear(&c->playlist);
725 mpd_send_list_queue_meta(c->connection);
726 while ((entity = mpd_recv_entity(c->connection))) {
727 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
728 playlist_append(&c->playlist, mpd_entity_get_song(entity));
730 mpd_entity_free(entity);
731 }
733 c->playlist.id = mpd_status_get_queue_version(c->status);
734 c->song = NULL;
736 /* call playlist updated callbacks */
737 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
739 return mpdclient_finish_command(c) == 0;
740 }
742 /* update playlist (plchanges) */
743 bool
744 mpdclient_playlist_update_changes(struct mpdclient *c)
745 {
746 struct mpd_song *song;
747 guint length;
749 if (MPD_ERROR(c))
750 return false;
752 mpd_send_queue_changes_meta(c->connection, c->playlist.id);
754 while ((song = mpd_recv_song(c->connection)) != NULL) {
755 int pos = mpd_song_get_pos(song);
757 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
758 /* update song */
759 playlist_replace(&c->playlist, pos, song);
760 } else {
761 /* add a new song */
762 playlist_append(&c->playlist, song);
763 }
765 mpd_song_free(song);
766 }
768 /* remove trailing songs */
770 length = mpd_status_get_queue_length(c->status);
771 while (length < c->playlist.list->len) {
772 guint pos = c->playlist.list->len - 1;
774 /* Remove the last playlist entry */
775 playlist_remove(&c->playlist, pos);
776 }
778 c->song = NULL;
779 c->playlist.id = mpd_status_get_queue_version(c->status);
781 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
783 return mpdclient_finish_command(c) == 0;
784 }
787 /****************************************************************************/
788 /*** Filelist functions *****************************************************/
789 /****************************************************************************/
791 struct filelist *
792 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
793 {
794 struct filelist *filelist;
795 struct mpd_entity *entity;
797 if (MPD_ERROR(c))
798 return NULL;
800 mpd_send_list_meta(c->connection, path);
801 filelist = filelist_new();
802 if (path && path[0] && strcmp(path, "/"))
803 /* add a dummy entry for ./.. */
804 filelist_append(filelist, NULL);
806 while ((entity = mpd_recv_entity(c->connection)) != NULL)
807 filelist_append(filelist, entity);
809 /* If there's an error, ignore it. We'll return an empty filelist. */
810 mpdclient_finish_command(c);
812 filelist_sort_dir_play(filelist, compare_filelistentry);
814 return filelist;
815 }
817 static struct filelist *
818 mpdclient_recv_filelist_response(struct mpdclient *c)
819 {
820 struct filelist *filelist;
821 struct mpd_entity *entity;
823 filelist = filelist_new();
825 while ((entity = mpd_recv_entity(c->connection)) != NULL)
826 filelist_append(filelist, entity);
828 if (mpdclient_finish_command(c)) {
829 filelist_free(filelist);
830 return NULL;
831 }
833 return filelist;
834 }
836 struct filelist *
837 mpdclient_filelist_search(struct mpdclient *c,
838 int exact_match,
839 enum mpd_tag_type tag,
840 gchar *filter_utf8)
841 {
842 if (MPD_ERROR(c))
843 return NULL;
845 mpd_search_db_songs(c->connection, exact_match);
846 mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
847 tag, filter_utf8);
848 mpd_search_commit(c->connection);
850 return mpdclient_recv_filelist_response(c);
851 }
853 int
854 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
855 {
856 guint i;
858 if (MPD_ERROR(c))
859 return -1;
861 if (filelist_is_empty(fl))
862 return 0;
864 mpd_command_list_begin(c->connection, false);
866 for (i = 0; i < filelist_length(fl); ++i) {
867 struct filelist_entry *entry = filelist_get(fl, i);
868 struct mpd_entity *entity = entry->entity;
870 if (entity != NULL &&
871 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
872 const struct mpd_song *song =
873 mpd_entity_get_song(entity);
874 const char *uri = mpd_song_get_uri(song);
876 if (uri != NULL)
877 mpd_send_add(c->connection, uri);
878 }
879 }
881 mpd_command_list_end(c->connection);
882 return mpdclient_finish_command(c);
883 }
885 GList *
886 mpdclient_get_artists(struct mpdclient *c)
887 {
888 GList *list = NULL;
889 struct mpd_pair *pair;
891 if (MPD_ERROR(c))
892 return NULL;
894 mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
895 mpd_search_commit(c->connection);
897 while ((pair = mpd_recv_pair_tag(c->connection,
898 MPD_TAG_ARTIST)) != NULL) {
899 list = g_list_append(list, g_strdup(pair->value));
900 mpd_return_pair(c->connection, pair);
901 }
903 if (mpdclient_finish_command(c))
904 return string_list_free(list);
906 return list;
907 }
909 GList *
910 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
911 {
912 GList *list = NULL;
913 struct mpd_pair *pair;
915 if (MPD_ERROR(c))
916 return NULL;
918 mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
919 if (artist_utf8 != NULL)
920 mpd_search_add_tag_constraint(c->connection,
921 MPD_OPERATOR_DEFAULT,
922 MPD_TAG_ARTIST, artist_utf8);
923 mpd_search_commit(c->connection);
925 while ((pair = mpd_recv_pair_tag(c->connection,
926 MPD_TAG_ALBUM)) != NULL) {
927 list = g_list_append(list, g_strdup(pair->value));
928 mpd_return_pair(c->connection, pair);
929 }
931 if (mpdclient_finish_command(c))
932 return string_list_free(list);
934 return list;
935 }