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 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 mpdclient_handle_error(c);
202 mpdclient_disconnect(c);
203 return false;
204 }
206 /* send password */
207 if( password ) {
208 mpd_send_password(c->connection, password);
209 retval = mpdclient_finish_command(c);
210 }
212 return true;
213 }
215 bool
216 mpdclient_update(struct mpdclient *c)
217 {
218 bool retval;
220 c->volume = MPD_STATUS_NO_VOLUME;
222 if (MPD_ERROR(c))
223 return false;
225 /* free the old status */
226 if (c->status)
227 mpd_status_free(c->status);
229 /* retrieve new status */
230 c->status = mpd_run_status(c->connection);
231 if (c->status == NULL)
232 return mpdclient_handle_error(c) == 0;
234 if (c->updatingdb &&
235 c->updatingdb != mpd_status_get_update_id(c->status))
236 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
238 c->updatingdb = mpd_status_get_update_id(c->status);
239 c->volume = mpd_status_get_volume(c->status);
241 /* check if the playlist needs an update */
242 if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
243 if (!playlist_is_empty(&c->playlist))
244 retval = mpdclient_playlist_update_changes(c);
245 else
246 retval = mpdclient_playlist_update(c);
247 }
249 /* update the current song */
250 if (!c->song || mpd_status_get_song_id(c->status)) {
251 c->song = playlist_get_song(&c->playlist,
252 mpd_status_get_song_pos(c->status));
253 }
255 return retval;
256 }
259 /****************************************************************************/
260 /*** MPD Commands **********************************************************/
261 /****************************************************************************/
263 gint
264 mpdclient_cmd_play(struct mpdclient *c, gint idx)
265 {
266 const struct mpd_song *song = playlist_get_song(&c->playlist, idx);
268 if (MPD_ERROR(c))
269 return -1;
271 if (song)
272 mpd_send_play_id(c->connection, mpd_song_get_id(song));
273 else
274 mpd_send_play(c->connection);
276 return mpdclient_finish_command(c);
277 }
279 gint
280 mpdclient_cmd_pause(struct mpdclient *c, gint value)
281 {
282 if (MPD_ERROR(c))
283 return -1;
285 mpd_send_pause(c->connection, value);
286 return mpdclient_finish_command(c);
287 }
289 gint
290 mpdclient_cmd_crop(struct mpdclient *c)
291 {
292 struct mpd_status *status;
293 bool playing;
294 int length, current;
296 if (MPD_ERROR(c))
297 return -1;
299 status = mpd_run_status(c->connection);
300 if (status == NULL)
301 return mpdclient_handle_error(c);
303 playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
304 mpd_status_get_state(status) == MPD_STATE_PAUSE;
305 length = mpd_status_get_queue_length(status);
306 current = mpd_status_get_song_pos(status);
308 mpd_status_free(status);
310 if (!playing || length < 2)
311 return 0;
313 mpd_command_list_begin(c->connection, false);
315 while (--length >= 0)
316 if (length != current)
317 mpd_send_delete(c->connection, length);
319 mpd_command_list_end(c->connection);
321 return mpdclient_finish_command(c);
322 }
324 gint
325 mpdclient_cmd_stop(struct mpdclient *c)
326 {
327 if (MPD_ERROR(c))
328 return -1;
330 mpd_send_stop(c->connection);
331 return mpdclient_finish_command(c);
332 }
334 gint
335 mpdclient_cmd_next(struct mpdclient *c)
336 {
337 if (MPD_ERROR(c))
338 return -1;
340 mpd_send_next(c->connection);
341 return mpdclient_finish_command(c);
342 }
344 gint
345 mpdclient_cmd_prev(struct mpdclient *c)
346 {
347 if (MPD_ERROR(c))
348 return -1;
350 mpd_send_previous(c->connection);
351 return mpdclient_finish_command(c);
352 }
354 gint
355 mpdclient_cmd_seek(struct mpdclient *c, gint id, gint pos)
356 {
357 if (MPD_ERROR(c))
358 return -1;
360 mpd_send_seek_id(c->connection, id, pos);
361 return mpdclient_finish_command(c);
362 }
364 gint
365 mpdclient_cmd_shuffle(struct mpdclient *c)
366 {
367 if (MPD_ERROR(c))
368 return -1;
370 mpd_send_shuffle(c->connection);
371 return mpdclient_finish_command(c);
372 }
374 gint
375 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
376 {
377 mpd_send_shuffle_range(c->connection, start, end);
378 return mpdclient_finish_command(c);
379 }
381 gint
382 mpdclient_cmd_clear(struct mpdclient *c)
383 {
384 gint retval = 0;
386 if (MPD_ERROR(c))
387 return -1;
389 mpd_send_clear(c->connection);
390 retval = mpdclient_finish_command(c);
391 /* call playlist updated callback */
392 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
393 return retval;
394 }
396 gint
397 mpdclient_cmd_repeat(struct mpdclient *c, gint value)
398 {
399 if (MPD_ERROR(c))
400 return -1;
402 mpd_send_repeat(c->connection, value);
403 return mpdclient_finish_command(c);
404 }
406 gint
407 mpdclient_cmd_random(struct mpdclient *c, gint value)
408 {
409 if (MPD_ERROR(c))
410 return -1;
412 mpd_send_random(c->connection, value);
413 return mpdclient_finish_command(c);
414 }
416 gint
417 mpdclient_cmd_single(struct mpdclient *c, gint value)
418 {
419 if (MPD_ERROR(c))
420 return -1;
422 mpd_send_single(c->connection, value);
423 return mpdclient_finish_command(c);
424 }
426 gint
427 mpdclient_cmd_consume(struct mpdclient *c, gint value)
428 {
429 if (MPD_ERROR(c))
430 return -1;
432 mpd_send_consume(c->connection, value);
433 return mpdclient_finish_command(c);
434 }
436 gint
437 mpdclient_cmd_crossfade(struct mpdclient *c, gint value)
438 {
439 if (MPD_ERROR(c))
440 return -1;
442 mpd_send_crossfade(c->connection, value);
443 return mpdclient_finish_command(c);
444 }
446 gint
447 mpdclient_cmd_volume(struct mpdclient *c, gint value)
448 {
449 if (MPD_ERROR(c))
450 return -1;
452 mpd_send_set_volume(c->connection, value);
453 return mpdclient_finish_command(c);
454 }
456 gint mpdclient_cmd_volume_up(struct mpdclient *c)
457 {
458 if (MPD_ERROR(c))
459 return -1;
461 if (c->status == NULL ||
462 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
463 return 0;
465 if (c->volume == MPD_STATUS_NO_VOLUME)
466 c->volume = mpd_status_get_volume(c->status);
468 if (c->volume >= 100)
469 return 0;
471 return mpdclient_cmd_volume(c, ++c->volume);
472 }
474 gint mpdclient_cmd_volume_down(struct mpdclient *c)
475 {
476 if (MPD_ERROR(c))
477 return -1;
479 if (c->status == NULL ||
480 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
481 return 0;
483 if (c->volume == MPD_STATUS_NO_VOLUME)
484 c->volume = mpd_status_get_volume(c->status);
486 if (c->volume <= 0)
487 return 0;
489 return mpdclient_cmd_volume(c, --c->volume);
490 }
492 gint
493 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
494 {
495 if (MPD_ERROR(c))
496 return -1;
498 mpd_send_add(c->connection, path_utf8);
499 return mpdclient_finish_command(c);
500 }
502 gint
503 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
504 {
505 gint retval = 0;
507 if (MPD_ERROR(c))
508 return -1;
510 if (song == NULL)
511 return -1;
513 /* send the add command to mpd */
514 mpd_send_add(c->connection, mpd_song_get_uri(song));
515 if( (retval=mpdclient_finish_command(c)) )
516 return retval;
518 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
519 /* add the song to playlist */
520 playlist_append(&c->playlist, song);
522 /* increment the playlist id, so we don't retrieve a new playlist */
523 c->playlist.id++;
525 /* call playlist updated callback */
526 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
527 #endif
529 return 0;
530 }
532 gint
533 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
534 {
535 gint retval = 0;
536 struct mpd_song *song;
538 if (MPD_ERROR(c))
539 return -1;
541 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
542 return -1;
544 song = playlist_get(&c->playlist, idx);
546 /* send the delete command to mpd */
547 mpd_send_delete_id(c->connection, mpd_song_get_id(song));
548 if( (retval=mpdclient_finish_command(c)) )
549 return retval;
551 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
552 /* increment the playlist id, so we don't retrieve a new playlist */
553 c->playlist.id++;
555 /* remove the song from the playlist */
556 playlist_remove_reuse(&c->playlist, idx);
558 /* call playlist updated callback */
559 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
561 /* remove references to the song */
562 if (c->song == song)
563 c->song = NULL;
565 mpd_song_free(song);
566 #endif
568 return 0;
569 }
571 gint
572 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
573 {
574 gint n;
575 struct mpd_song *song1, *song2;
577 if (MPD_ERROR(c))
578 return -1;
580 if (old_index == new_index || new_index < 0 ||
581 (guint)new_index >= c->playlist.list->len)
582 return -1;
584 song1 = playlist_get(&c->playlist, old_index);
585 song2 = playlist_get(&c->playlist, new_index);
587 /* send the move command to mpd */
588 mpd_send_swap_id(c->connection,
589 mpd_song_get_id(song1), mpd_song_get_id(song2));
590 if( (n=mpdclient_finish_command(c)) )
591 return n;
593 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
594 /* update the playlist */
595 playlist_swap(&c->playlist, old_index, new_index);
597 /* increment the playlist id, so we don't retrieve a new playlist */
598 c->playlist.id++;
599 #endif
601 /* call playlist updated callback */
602 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
604 return 0;
605 }
607 gint
608 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
609 {
610 gint retval = 0;
612 if (MPD_ERROR(c))
613 return -1;
615 mpd_send_save(c->connection, filename_utf8);
616 if ((retval = mpdclient_finish_command(c)) == 0)
617 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
618 return retval;
619 }
621 gint
622 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
623 {
624 if (MPD_ERROR(c))
625 return -1;
627 mpd_send_load(c->connection, filename_utf8);
628 return mpdclient_finish_command(c);
629 }
631 gint
632 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
633 {
634 gint retval = 0;
636 if (MPD_ERROR(c))
637 return -1;
639 mpd_send_rm(c->connection, filename_utf8);
640 if ((retval = mpdclient_finish_command(c)) == 0)
641 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
642 return retval;
643 }
646 /****************************************************************************/
647 /*** Callback management functions ******************************************/
648 /****************************************************************************/
650 static void
651 do_list_callbacks(struct mpdclient *c, GList *list, gint event, gpointer data)
652 {
653 while (list) {
654 mpdc_list_cb_t fn = list->data;
656 fn(c, event, data);
657 list = list->next;
658 }
659 }
661 void
662 mpdclient_playlist_callback(struct mpdclient *c, int event, gpointer data)
663 {
664 do_list_callbacks(c, c->playlist_callbacks, event, data);
665 }
667 void
668 mpdclient_install_playlist_callback(struct mpdclient *c,mpdc_list_cb_t cb)
669 {
670 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
671 }
673 void
674 mpdclient_remove_playlist_callback(struct mpdclient *c, mpdc_list_cb_t cb)
675 {
676 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
677 }
679 void
680 mpdclient_browse_callback(struct mpdclient *c, int event, gpointer data)
681 {
682 do_list_callbacks(c, c->browse_callbacks, event, data);
683 }
686 void
687 mpdclient_install_browse_callback(struct mpdclient *c,mpdc_list_cb_t cb)
688 {
689 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
690 }
692 void
693 mpdclient_remove_browse_callback(struct mpdclient *c, mpdc_list_cb_t cb)
694 {
695 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
696 }
698 void
699 mpdclient_install_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
700 {
701 c->error_callbacks = g_list_append(c->error_callbacks, cb);
702 }
704 void
705 mpdclient_remove_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
706 {
707 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
708 }
711 /****************************************************************************/
712 /*** Playlist management functions ******************************************/
713 /****************************************************************************/
715 /* update playlist */
716 bool
717 mpdclient_playlist_update(struct mpdclient *c)
718 {
719 struct mpd_entity *entity;
721 if (MPD_ERROR(c))
722 return false;
724 playlist_clear(&c->playlist);
726 mpd_send_list_queue_meta(c->connection);
727 while ((entity = mpd_recv_entity(c->connection))) {
728 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
729 playlist_append(&c->playlist, mpd_entity_get_song(entity));
731 mpd_entity_free(entity);
732 }
734 c->playlist.id = mpd_status_get_queue_version(c->status);
735 c->song = NULL;
737 /* call playlist updated callbacks */
738 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
740 return mpdclient_finish_command(c) == 0;
741 }
743 /* update playlist (plchanges) */
744 bool
745 mpdclient_playlist_update_changes(struct mpdclient *c)
746 {
747 struct mpd_song *song;
748 guint length;
750 if (MPD_ERROR(c))
751 return false;
753 mpd_send_queue_changes_meta(c->connection, c->playlist.id);
755 while ((song = mpd_recv_song(c->connection)) != NULL) {
756 int pos = mpd_song_get_pos(song);
758 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
759 /* update song */
760 playlist_replace(&c->playlist, pos, song);
761 } else {
762 /* add a new song */
763 playlist_append(&c->playlist, song);
764 }
766 mpd_song_free(song);
767 }
769 /* remove trailing songs */
771 length = mpd_status_get_queue_length(c->status);
772 while (length < c->playlist.list->len) {
773 guint pos = c->playlist.list->len - 1;
775 /* Remove the last playlist entry */
776 playlist_remove(&c->playlist, pos);
777 }
779 c->song = NULL;
780 c->playlist.id = mpd_status_get_queue_version(c->status);
782 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
784 return mpdclient_finish_command(c) == 0;
785 }
788 /****************************************************************************/
789 /*** Filelist functions *****************************************************/
790 /****************************************************************************/
792 struct filelist *
793 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
794 {
795 struct filelist *filelist;
796 struct mpd_entity *entity;
798 if (MPD_ERROR(c))
799 return NULL;
801 mpd_send_list_meta(c->connection, path);
802 filelist = filelist_new();
803 if (path && path[0] && strcmp(path, "/"))
804 /* add a dummy entry for ./.. */
805 filelist_append(filelist, NULL);
807 while ((entity = mpd_recv_entity(c->connection)) != NULL)
808 filelist_append(filelist, entity);
810 /* If there's an error, ignore it. We'll return an empty filelist. */
811 mpdclient_finish_command(c);
813 filelist_sort_dir_play(filelist, compare_filelistentry);
815 return filelist;
816 }
818 static struct filelist *
819 mpdclient_recv_filelist_response(struct mpdclient *c)
820 {
821 struct filelist *filelist;
822 struct mpd_entity *entity;
824 filelist = filelist_new();
826 while ((entity = mpd_recv_entity(c->connection)) != NULL)
827 filelist_append(filelist, entity);
829 if (mpdclient_finish_command(c)) {
830 filelist_free(filelist);
831 return NULL;
832 }
834 return filelist;
835 }
837 struct filelist *
838 mpdclient_filelist_search(struct mpdclient *c,
839 int exact_match,
840 enum mpd_tag_type tag,
841 gchar *filter_utf8)
842 {
843 if (MPD_ERROR(c))
844 return NULL;
846 mpd_search_db_songs(c->connection, exact_match);
847 mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
848 tag, filter_utf8);
849 mpd_search_commit(c->connection);
851 return mpdclient_recv_filelist_response(c);
852 }
854 int
855 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
856 {
857 guint i;
859 if (MPD_ERROR(c))
860 return -1;
862 if (filelist_is_empty(fl))
863 return 0;
865 mpd_command_list_begin(c->connection, false);
867 for (i = 0; i < filelist_length(fl); ++i) {
868 struct filelist_entry *entry = filelist_get(fl, i);
869 struct mpd_entity *entity = entry->entity;
871 if (entity != NULL &&
872 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
873 const struct mpd_song *song =
874 mpd_entity_get_song(entity);
875 const char *uri = mpd_song_get_uri(song);
877 if (uri != NULL)
878 mpd_send_add(c->connection, uri);
879 }
880 }
882 mpd_command_list_end(c->connection);
883 return mpdclient_finish_command(c);
884 }
886 GList *
887 mpdclient_get_artists(struct mpdclient *c)
888 {
889 GList *list = NULL;
890 struct mpd_pair *pair;
892 if (MPD_ERROR(c))
893 return NULL;
895 mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
896 mpd_search_commit(c->connection);
898 while ((pair = mpd_recv_pair_tag(c->connection,
899 MPD_TAG_ARTIST)) != NULL) {
900 list = g_list_append(list, g_strdup(pair->value));
901 mpd_return_pair(c->connection, pair);
902 }
904 if (mpdclient_finish_command(c))
905 return string_list_free(list);
907 return list;
908 }
910 GList *
911 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
912 {
913 GList *list = NULL;
914 struct mpd_pair *pair;
916 if (MPD_ERROR(c))
917 return NULL;
919 mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
920 if (artist_utf8 != NULL)
921 mpd_search_add_tag_constraint(c->connection,
922 MPD_OPERATOR_DEFAULT,
923 MPD_TAG_ARTIST, artist_utf8);
924 mpd_search_commit(c->connection);
926 while ((pair = mpd_recv_pair_tag(c->connection,
927 MPD_TAG_ALBUM)) != NULL) {
928 list = g_list_append(list, g_strdup(pair->value));
929 mpd_return_pair(c->connection, pair);
930 }
932 if (mpdclient_finish_command(c))
933 return string_list_free(list);
935 return list;
936 }