1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2017 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.
20 #include "mpdclient.h"
21 #include "callbacks.h"
27 #ifdef ENABLE_ASYNC_CONNECT
31 #include <mpd/client.h>
36 mpdclient_enter_idle_callback(gpointer user_data
)
38 struct mpdclient
*c
= user_data
;
39 assert(c
->enter_idle_source_id
!= 0);
40 assert(c
->source
!= NULL
);
43 c
->enter_idle_source_id
= 0;
44 c
->idle
= mpd_glib_enter(c
->source
);
49 mpdclient_schedule_enter_idle(struct mpdclient
*c
)
52 assert(c
->source
!= NULL
);
54 if (c
->enter_idle_source_id
== 0)
55 /* automatically re-enter MPD "idle" mode */
56 c
->enter_idle_source_id
=
57 g_idle_add(mpdclient_enter_idle_callback
, c
);
61 mpdclient_cancel_enter_idle(struct mpdclient
*c
)
63 if (c
->enter_idle_source_id
!= 0) {
64 g_source_remove(c
->enter_idle_source_id
);
65 c
->enter_idle_source_id
= 0;
70 mpdclient_invoke_error_callback(enum mpd_error error
,
74 if (error
== MPD_ERROR_SERVER
)
75 /* server errors are UTF-8, the others are locale */
76 message
= allocated
= utf8_to_locale(message
);
80 mpdclient_error_callback(message
);
85 mpdclient_invoke_error_callback1(struct mpdclient
*c
)
88 assert(c
->connection
!= NULL
);
90 struct mpd_connection
*connection
= c
->connection
;
92 enum mpd_error error
= mpd_connection_get_error(connection
);
93 assert(error
!= MPD_ERROR_SUCCESS
);
95 mpdclient_invoke_error_callback(error
,
96 mpd_connection_get_error_message(connection
));
100 mpdclient_gidle_callback(enum mpd_error error
,
101 gcc_unused
enum mpd_server_error server_error
,
102 const char *message
, enum mpd_idle events
,
105 struct mpdclient
*c
= ctx
;
109 assert(mpdclient_is_connected(c
));
111 if (error
!= MPD_ERROR_SUCCESS
) {
112 mpdclient_invoke_error_callback(error
, message
);
113 mpdclient_disconnect(c
);
114 mpdclient_lost_callback();
121 mpdclient_idle_callback(c
->events
);
125 if (c
->source
!= NULL
)
126 mpdclient_schedule_enter_idle(c
);
129 /****************************************************************************/
130 /*** mpdclient functions ****************************************************/
131 /****************************************************************************/
134 mpdclient_handle_error(struct mpdclient
*c
)
136 enum mpd_error error
= mpd_connection_get_error(c
->connection
);
138 assert(error
!= MPD_ERROR_SUCCESS
);
140 if (error
== MPD_ERROR_SERVER
&&
141 mpd_connection_get_server_error(c
->connection
) == MPD_SERVER_ERROR_PERMISSION
&&
142 mpdclient_auth_callback(c
))
145 mpdclient_invoke_error_callback(error
,
146 mpd_connection_get_error_message(c
->connection
));
148 if (!mpd_connection_clear_error(c
->connection
)) {
149 mpdclient_disconnect(c
);
150 mpdclient_lost_callback();
156 #ifdef ENABLE_ASYNC_CONNECT
160 is_local_socket(const char *host
)
162 return *host
== '/' || *host
== '@';
166 settings_is_local_socket(const struct mpd_settings
*settings
)
168 const char *host
= mpd_settings_get_host(settings
);
169 return host
!= NULL
&& is_local_socket(host
);
176 mpdclient_new(const gchar
*host
, unsigned port
,
177 unsigned timeout_ms
, const gchar
*password
)
179 struct mpdclient
*c
= g_new0(struct mpdclient
, 1);
181 #ifdef ENABLE_ASYNC_CONNECT
182 c
->settings
= mpd_settings_new(host
, port
, timeout_ms
,
184 if (c
->settings
== NULL
)
185 g_error("Out of memory");
188 c
->settings2
= host
== NULL
&& port
== 0 &&
189 settings_is_local_socket(c
->settings
)
190 ? mpd_settings_new(host
, 6600, timeout_ms
, NULL
, NULL
)
199 c
->timeout_ms
= timeout_ms
;
200 c
->password
= password
;
202 playlist_init(&c
->playlist
);
211 mpdclient_free(struct mpdclient
*c
)
213 mpdclient_disconnect(c
);
215 mpdclient_playlist_free(&c
->playlist
);
217 #ifdef ENABLE_ASYNC_CONNECT
218 mpd_settings_free(c
->settings
);
221 if (c
->settings2
!= NULL
)
222 mpd_settings_free(c
->settings2
);
230 settings_name(const struct mpd_settings
*settings
)
232 assert(settings
!= NULL
);
234 const char *host
= mpd_settings_get_host(settings
);
239 return g_strdup(host
);
241 unsigned port
= mpd_settings_get_port(settings
);
242 if (port
== 0 || port
== 6600)
243 return g_strdup(host
);
245 return g_strdup_printf("%s:%u", host
, port
);
249 mpdclient_settings_name(const struct mpdclient
*c
)
253 #ifdef ENABLE_ASYNC_CONNECT
254 return settings_name(c
->settings
);
256 struct mpd_settings
*settings
=
257 mpd_settings_new(c
->host
, c
->port
, 0, NULL
, NULL
);
258 if (settings
== NULL
)
259 return g_strdup("unknown");
261 char *name
= settings_name(settings
);
262 mpd_settings_free(settings
);
268 mpdclient_status_free(struct mpdclient
*c
)
270 if (c
->status
== NULL
)
273 mpd_status_free(c
->status
);
281 mpdclient_disconnect(struct mpdclient
*c
)
283 #ifdef ENABLE_ASYNC_CONNECT
284 if (c
->async_connect
!= NULL
) {
285 aconnect_cancel(c
->async_connect
);
286 c
->async_connect
= NULL
;
290 mpdclient_cancel_enter_idle(c
);
292 if (c
->source
!= NULL
) {
293 mpd_glib_free(c
->source
);
299 mpd_connection_free(c
->connection
);
302 c
->connection
= NULL
;
304 mpdclient_status_free(c
);
306 playlist_clear(&c
->playlist
);
311 /* everything has changed after a disconnect */
312 c
->events
|= MPD_IDLE_ALL
;
316 mpdclient_connected(struct mpdclient
*c
,
317 struct mpd_connection
*connection
)
319 c
->connection
= connection
;
321 if (mpd_connection_get_error(connection
) != MPD_ERROR_SUCCESS
) {
322 mpdclient_invoke_error_callback1(c
);
323 mpdclient_disconnect(c
);
324 mpdclient_failed_callback();
328 #ifdef ENABLE_ASYNC_CONNECT
329 if (c
->timeout_ms
> 0)
330 mpd_connection_set_timeout(connection
, c
->timeout_ms
);
334 if (c
->password
!= NULL
&&
335 !mpd_run_password(connection
, c
->password
)) {
336 mpdclient_invoke_error_callback1(c
);
337 mpdclient_disconnect(c
);
338 mpdclient_failed_callback();
342 c
->source
= mpd_glib_new(connection
,
343 mpdclient_gidle_callback
, c
);
344 mpdclient_schedule_enter_idle(c
);
348 mpdclient_connected_callback();
352 #ifdef ENABLE_ASYNC_CONNECT
355 mpdclient_aconnect_start(struct mpdclient
*c
,
356 const struct mpd_settings
*settings
);
359 mpdclient_connect_success(struct mpd_connection
*connection
, void *ctx
)
361 struct mpdclient
*c
= ctx
;
362 assert(c
->async_connect
!= NULL
);
363 c
->async_connect
= NULL
;
365 mpdclient_connected(c
, connection
);
369 mpdclient_connect_error(const char *message
, void *ctx
)
371 struct mpdclient
*c
= ctx
;
372 assert(c
->async_connect
!= NULL
);
373 c
->async_connect
= NULL
;
376 if (!c
->connecting2
&& c
->settings2
!= NULL
) {
377 c
->connecting2
= true;
378 mpdclient_aconnect_start(c
, c
->settings2
);
383 mpdclient_error_callback(message
);
384 mpdclient_failed_callback();
387 static const struct aconnect_handler mpdclient_connect_handler
= {
388 .success
= mpdclient_connect_success
,
389 .error
= mpdclient_connect_error
,
393 mpdclient_aconnect_start(struct mpdclient
*c
,
394 const struct mpd_settings
*settings
)
396 aconnect_start(&c
->async_connect
,
397 mpd_settings_get_host(settings
),
398 mpd_settings_get_port(settings
),
399 &mpdclient_connect_handler
, c
);
405 mpdclient_connect(struct mpdclient
*c
)
407 /* close any open connection */
408 mpdclient_disconnect(c
);
410 #ifdef ENABLE_ASYNC_CONNECT
412 c
->connecting2
= false;
414 mpdclient_aconnect_start(c
, c
->settings
);
417 struct mpd_connection
*connection
=
418 mpd_connection_new(c
->host
, c
->port
, c
->timeout_ms
);
419 if (connection
== NULL
)
420 g_error("Out of memory");
422 mpdclient_connected(c
, connection
);
427 mpdclient_update(struct mpdclient
*c
)
429 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
431 if (connection
== NULL
)
434 /* free the old status */
435 mpdclient_status_free(c
);
437 /* retrieve new status */
438 c
->status
= mpd_run_status(connection
);
439 if (c
->status
== NULL
)
440 return mpdclient_handle_error(c
);
442 c
->volume
= mpd_status_get_volume(c
->status
);
443 c
->playing
= mpd_status_get_state(c
->status
) == MPD_STATE_PLAY
;
445 /* check if the playlist needs an update */
446 if (c
->playlist
.version
!= mpd_status_get_queue_version(c
->status
)) {
449 if (!playlist_is_empty(&c
->playlist
))
450 retval
= mpdclient_playlist_update_changes(c
);
452 retval
= mpdclient_playlist_update(c
);
457 /* update the current song */
458 if (!c
->song
|| mpd_status_get_song_id(c
->status
) >= 0) {
459 c
->song
= playlist_get_song(&c
->playlist
,
460 mpd_status_get_song_pos(c
->status
));
466 struct mpd_connection
*
467 mpdclient_get_connection(struct mpdclient
*c
)
469 if (c
->source
!= NULL
&& c
->idle
) {
471 mpd_glib_leave(c
->source
);
473 mpdclient_schedule_enter_idle(c
);
476 return c
->connection
;
479 static struct mpd_status
*
480 mpdclient_recv_status(struct mpdclient
*c
)
482 assert(c
->connection
!= NULL
);
484 struct mpd_status
*status
= mpd_recv_status(c
->connection
);
485 if (status
== NULL
) {
486 mpdclient_handle_error(c
);
490 if (c
->status
!= NULL
)
491 mpd_status_free(c
->status
);
492 return c
->status
= status
;
495 /****************************************************************************/
496 /*** MPD Commands **********************************************************/
497 /****************************************************************************/
500 mpdclient_cmd_crop(struct mpdclient
*c
)
502 if (!mpdclient_is_playing(c
))
505 int length
= mpd_status_get_queue_length(c
->status
);
506 int current
= mpd_status_get_song_pos(c
->status
);
507 if (current
< 0 || mpd_status_get_queue_length(c
->status
) < 2)
510 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
511 if (connection
== NULL
)
514 mpd_command_list_begin(connection
, false);
516 if (current
< length
- 1)
517 mpd_send_delete_range(connection
, current
+ 1, length
);
519 mpd_send_delete_range(connection
, 0, current
);
521 mpd_command_list_end(connection
);
523 return mpdclient_finish_command(c
);
527 mpdclient_cmd_clear(struct mpdclient
*c
)
529 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
530 if (connection
== NULL
)
533 /* send "clear" and "status" */
534 if (!mpd_command_list_begin(connection
, false) ||
535 !mpd_send_clear(connection
) ||
536 !mpd_send_status(connection
) ||
537 !mpd_command_list_end(connection
))
538 return mpdclient_handle_error(c
);
540 /* receive the new status, store it in the mpdclient struct */
542 struct mpd_status
*status
= mpdclient_recv_status(c
);
546 if (!mpd_response_finish(connection
))
547 return mpdclient_handle_error(c
);
549 /* update mpdclient.playlist */
551 if (mpd_status_get_queue_length(status
) == 0) {
552 /* after the "clear" command, the queue is really
553 empty - this means we can clear it locally,
554 reducing the UI latency */
555 playlist_clear(&c
->playlist
);
556 c
->playlist
.version
= mpd_status_get_queue_version(status
);
560 c
->events
|= MPD_IDLE_QUEUE
;
565 mpdclient_cmd_volume(struct mpdclient
*c
, gint value
)
567 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
568 if (connection
== NULL
)
571 mpd_send_set_volume(connection
, value
);
572 return mpdclient_finish_command(c
);
576 mpdclient_cmd_volume_up(struct mpdclient
*c
)
578 if (c
->volume
< 0 || c
->volume
>= 100)
581 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
582 if (connection
== NULL
)
585 return mpdclient_cmd_volume(c
, ++c
->volume
);
589 mpdclient_cmd_volume_down(struct mpdclient
*c
)
594 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
595 if (connection
== NULL
)
598 return mpdclient_cmd_volume(c
, --c
->volume
);
602 mpdclient_cmd_add_path(struct mpdclient
*c
, const gchar
*path_utf8
)
604 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
605 if (connection
== NULL
)
608 return mpd_send_add(connection
, path_utf8
)?
609 mpdclient_finish_command(c
) : false;
613 mpdclient_cmd_add(struct mpdclient
*c
, const struct mpd_song
*song
)
616 assert(song
!= NULL
);
618 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
619 if (connection
== NULL
|| c
->status
== NULL
)
622 /* send the add command to mpd; at the same time, get the new
623 status (to verify the new playlist id) and the last song
624 (we hope that's the song we just added) */
626 if (!mpd_command_list_begin(connection
, true) ||
627 !mpd_send_add(connection
, mpd_song_get_uri(song
)) ||
628 !mpd_send_status(connection
) ||
629 !mpd_send_get_queue_song_pos(connection
,
630 playlist_length(&c
->playlist
)) ||
631 !mpd_command_list_end(connection
) ||
632 !mpd_response_next(connection
))
633 return mpdclient_handle_error(c
);
635 c
->events
|= MPD_IDLE_QUEUE
;
637 struct mpd_status
*status
= mpdclient_recv_status(c
);
641 if (!mpd_response_next(connection
))
642 return mpdclient_handle_error(c
);
644 struct mpd_song
*new_song
= mpd_recv_song(connection
);
645 if (!mpd_response_finish(connection
) || new_song
== NULL
) {
646 if (new_song
!= NULL
)
647 mpd_song_free(new_song
);
649 return mpd_connection_clear_error(connection
) ||
650 mpdclient_handle_error(c
);
653 if (mpd_status_get_queue_length(status
) == playlist_length(&c
->playlist
) + 1 &&
654 mpd_status_get_queue_version(status
) == c
->playlist
.version
+ 1) {
655 /* the cheap route: match on the new playlist length
656 and its version, we can keep our local playlist
658 c
->playlist
.version
= mpd_status_get_queue_version(status
);
660 /* the song we just received has the correct id;
661 append it to the local playlist */
662 playlist_append(&c
->playlist
, new_song
);
665 mpd_song_free(new_song
);
671 mpdclient_cmd_delete(struct mpdclient
*c
, gint idx
)
673 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
675 if (connection
== NULL
|| c
->status
== NULL
)
678 if (idx
< 0 || (guint
)idx
>= playlist_length(&c
->playlist
))
681 const struct mpd_song
*song
= playlist_get(&c
->playlist
, idx
);
683 /* send the delete command to mpd; at the same time, get the
684 new status (to verify the playlist id) */
686 if (!mpd_command_list_begin(connection
, false) ||
687 !mpd_send_delete_id(connection
, mpd_song_get_id(song
)) ||
688 !mpd_send_status(connection
) ||
689 !mpd_command_list_end(connection
))
690 return mpdclient_handle_error(c
);
692 c
->events
|= MPD_IDLE_QUEUE
;
694 struct mpd_status
*status
= mpdclient_recv_status(c
);
698 if (!mpd_response_finish(connection
))
699 return mpdclient_handle_error(c
);
701 if (mpd_status_get_queue_length(status
) == playlist_length(&c
->playlist
) - 1 &&
702 mpd_status_get_queue_version(status
) == c
->playlist
.version
+ 1) {
703 /* the cheap route: match on the new playlist length
704 and its version, we can keep our local playlist
706 c
->playlist
.version
= mpd_status_get_queue_version(status
);
708 /* remove the song from the local playlist */
709 playlist_remove(&c
->playlist
, idx
);
711 /* remove references to the song */
720 mpdclient_cmd_delete_range(struct mpdclient
*c
, unsigned start
, unsigned end
)
722 if (end
== start
+ 1)
723 /* if that's not really a range, we choose to use the
724 safer "deleteid" version */
725 return mpdclient_cmd_delete(c
, start
);
727 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
728 if (connection
== NULL
)
731 /* send the delete command to mpd; at the same time, get the
732 new status (to verify the playlist id) */
734 if (!mpd_command_list_begin(connection
, false) ||
735 !mpd_send_delete_range(connection
, start
, end
) ||
736 !mpd_send_status(connection
) ||
737 !mpd_command_list_end(connection
))
738 return mpdclient_handle_error(c
);
740 c
->events
|= MPD_IDLE_QUEUE
;
742 struct mpd_status
*status
= mpdclient_recv_status(c
);
746 if (!mpd_response_finish(connection
))
747 return mpdclient_handle_error(c
);
749 if (mpd_status_get_queue_length(status
) == playlist_length(&c
->playlist
) - (end
- start
) &&
750 mpd_status_get_queue_version(status
) == c
->playlist
.version
+ 1) {
751 /* the cheap route: match on the new playlist length
752 and its version, we can keep our local playlist
754 c
->playlist
.version
= mpd_status_get_queue_version(status
);
756 /* remove the song from the local playlist */
757 while (end
> start
) {
760 /* remove references to the song */
761 if (c
->song
== playlist_get(&c
->playlist
, end
))
764 playlist_remove(&c
->playlist
, end
);
772 mpdclient_cmd_move(struct mpdclient
*c
, unsigned dest_pos
, unsigned src_pos
)
774 if (dest_pos
== src_pos
)
777 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
778 if (connection
== NULL
)
781 /* send the "move" command to MPD; at the same time, get the
782 new status (to verify the playlist id) */
784 if (!mpd_command_list_begin(connection
, false) ||
785 !mpd_send_move(connection
, src_pos
, dest_pos
) ||
786 !mpd_send_status(connection
) ||
787 !mpd_command_list_end(connection
))
788 return mpdclient_handle_error(c
);
790 c
->events
|= MPD_IDLE_QUEUE
;
792 struct mpd_status
*status
= mpdclient_recv_status(c
);
796 if (!mpd_response_finish(connection
))
797 return mpdclient_handle_error(c
);
799 if (mpd_status_get_queue_length(status
) == playlist_length(&c
->playlist
) &&
800 mpd_status_get_queue_version(status
) == c
->playlist
.version
+ 1) {
801 /* the cheap route: match on the new playlist length
802 and its version, we can keep our local playlist
804 c
->playlist
.version
= mpd_status_get_queue_version(status
);
806 /* swap songs in the local playlist */
807 playlist_move(&c
->playlist
, dest_pos
, src_pos
);
813 /* The client-to-client protocol (MPD 0.17.0) */
816 mpdclient_cmd_subscribe(struct mpdclient
*c
, const char *channel
)
818 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
820 if (connection
== NULL
)
823 if (!mpd_send_subscribe(connection
, channel
))
824 return mpdclient_handle_error(c
);
826 return mpdclient_finish_command(c
);
830 mpdclient_cmd_unsubscribe(struct mpdclient
*c
, const char *channel
)
832 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
833 if (connection
== NULL
)
836 if (!mpd_send_unsubscribe(connection
, channel
))
837 return mpdclient_handle_error(c
);
839 return mpdclient_finish_command(c
);
843 mpdclient_cmd_send_message(struct mpdclient
*c
, const char *channel
,
846 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
847 if (connection
== NULL
)
850 if (!mpd_send_send_message(connection
, channel
, text
))
851 return mpdclient_handle_error(c
);
853 return mpdclient_finish_command(c
);
857 mpdclient_send_read_messages(struct mpdclient
*c
)
859 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
860 if (connection
== NULL
)
863 return mpd_send_read_messages(connection
)?
864 true : mpdclient_handle_error(c
);
868 mpdclient_recv_message(struct mpdclient
*c
)
870 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
871 if (connection
== NULL
)
874 struct mpd_message
*message
= mpd_recv_message(connection
);
875 if (message
== NULL
&&
876 mpd_connection_get_error(connection
) != MPD_ERROR_SUCCESS
)
877 mpdclient_handle_error(c
);
882 /****************************************************************************/
883 /*** Playlist management functions ******************************************/
884 /****************************************************************************/
886 /* update playlist */
888 mpdclient_playlist_update(struct mpdclient
*c
)
890 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
891 if (connection
== NULL
)
894 playlist_clear(&c
->playlist
);
896 mpd_send_list_queue_meta(connection
);
898 struct mpd_entity
*entity
;
899 while ((entity
= mpd_recv_entity(connection
))) {
900 if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
)
901 playlist_append(&c
->playlist
, mpd_entity_get_song(entity
));
903 mpd_entity_free(entity
);
906 c
->playlist
.version
= mpd_status_get_queue_version(c
->status
);
909 return mpdclient_finish_command(c
);
912 /* update playlist (plchanges) */
914 mpdclient_playlist_update_changes(struct mpdclient
*c
)
916 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
918 if (connection
== NULL
)
921 mpd_send_queue_changes_meta(connection
, c
->playlist
.version
);
923 struct mpd_song
*song
;
924 while ((song
= mpd_recv_song(connection
)) != NULL
) {
925 int pos
= mpd_song_get_pos(song
);
927 if (pos
>= 0 && (guint
)pos
< c
->playlist
.list
->len
) {
929 playlist_replace(&c
->playlist
, pos
, song
);
932 playlist_append(&c
->playlist
, song
);
938 /* remove trailing songs */
940 unsigned length
= mpd_status_get_queue_length(c
->status
);
941 while (length
< c
->playlist
.list
->len
) {
942 guint pos
= c
->playlist
.list
->len
- 1;
944 /* Remove the last playlist entry */
945 playlist_remove(&c
->playlist
, pos
);
949 c
->playlist
.version
= mpd_status_get_queue_version(c
->status
);
951 return mpdclient_finish_command(c
);
955 /****************************************************************************/
956 /*** Filelist functions *****************************************************/
957 /****************************************************************************/
960 mpdclient_filelist_add_all(struct mpdclient
*c
, struct filelist
*fl
)
962 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
963 if (connection
== NULL
)
966 if (filelist_is_empty(fl
))
969 mpd_command_list_begin(connection
, false);
971 for (unsigned i
= 0; i
< filelist_length(fl
); ++i
) {
972 struct filelist_entry
*entry
= filelist_get(fl
, i
);
973 struct mpd_entity
*entity
= entry
->entity
;
975 if (entity
!= NULL
&&
976 mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
) {
977 const struct mpd_song
*song
=
978 mpd_entity_get_song(entity
);
980 mpd_send_add(connection
, mpd_song_get_uri(song
));
984 mpd_command_list_end(connection
);
985 return mpdclient_finish_command(c
);