1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2010 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"
22 #include "screen_client.h"
29 #include <mpd/client.h>
38 /* sort by song format */
40 compare_filelistentry_format(gconstpointer filelist_entry1
,
41 gconstpointer filelist_entry2
,
42 const char *song_format
)
44 const struct mpd_entity
*e1
=
45 ((const struct filelist_entry
*)filelist_entry1
)->entity
;
46 const struct mpd_entity
*e2
=
47 ((const struct filelist_entry
*)filelist_entry2
)->entity
;
51 mpd_entity_get_type(e1
) == MPD_ENTITY_TYPE_SONG
&&
52 mpd_entity_get_type(e2
) == MPD_ENTITY_TYPE_SONG
) {
53 char key1
[BUFSIZE
], key2
[BUFSIZE
];
54 strfsong(key1
, BUFSIZE
, song_format
, mpd_entity_get_song(e1
));
55 strfsong(key2
, BUFSIZE
, song_format
, mpd_entity_get_song(e2
));
56 n
= strcmp(key1
,key2
);
63 /****************************************************************************/
64 /*** mpdclient functions ****************************************************/
65 /****************************************************************************/
68 mpdclient_handle_error(struct mpdclient
*c
)
70 enum mpd_error error
= mpd_connection_get_error(c
->connection
);
72 assert(error
!= MPD_ERROR_SUCCESS
);
74 if (error
== MPD_ERROR_SERVER
&&
75 mpd_connection_get_server_error(c
->connection
) == MPD_SERVER_ERROR_PERMISSION
&&
79 mpdclient_ui_error(mpd_connection_get_error_message(c
->connection
));
81 if (!mpd_connection_clear_error(c
->connection
))
82 mpdclient_disconnect(c
);
90 struct mpdclient
*c
= g_new0(struct mpdclient
, 1);
91 playlist_init(&c
->playlist
);
99 mpdclient_free(struct mpdclient
*c
)
101 mpdclient_disconnect(c
);
103 mpdclient_playlist_free(&c
->playlist
);
109 mpdclient_disconnect(struct mpdclient
*c
)
111 if (c
->source
!= NULL
) {
112 mpd_glib_free(c
->source
);
118 mpd_connection_free(c
->connection
);
121 c
->connection
= NULL
;
124 mpd_status_free(c
->status
);
127 playlist_clear(&c
->playlist
);
132 /* everything has changed after a disconnect */
133 c
->events
|= MPD_IDLE_ALL
;
137 mpdclient_connect(struct mpdclient
*c
,
141 const gchar
*password
)
143 /* close any open connection */
145 mpdclient_disconnect(c
);
148 c
->connection
= mpd_connection_new(host
, port
, timeout_ms
);
149 if (c
->connection
== NULL
)
150 g_error("Out of memory");
152 if (mpd_connection_get_error(c
->connection
) != MPD_ERROR_SUCCESS
) {
153 mpdclient_handle_error(c
);
154 mpdclient_disconnect(c
);
159 if (password
!= NULL
&& !mpd_run_password(c
->connection
, password
)) {
160 mpdclient_handle_error(c
);
161 mpdclient_disconnect(c
);
171 mpdclient_update(struct mpdclient
*c
)
173 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
177 if (connection
== NULL
)
180 /* always announce these options as long as we don't have
182 if (c
->source
== NULL
)
183 c
->events
|= MPD_IDLE_PLAYER
|MPD_IDLE_OPTIONS
;
185 /* free the old status */
187 mpd_status_free(c
->status
);
189 /* retrieve new status */
190 c
->status
= mpd_run_status(connection
);
191 if (c
->status
== NULL
)
192 return mpdclient_handle_error(c
);
194 if (c
->source
== NULL
&&
195 c
->update_id
!= mpd_status_get_update_id(c
->status
)) {
196 c
->events
|= MPD_IDLE_UPDATE
;
198 if (c
->update_id
> 0)
199 c
->events
|= MPD_IDLE_DATABASE
;
202 c
->update_id
= mpd_status_get_update_id(c
->status
);
204 if (c
->source
== NULL
&&
205 c
->volume
!= mpd_status_get_volume(c
->status
))
206 c
->events
|= MPD_IDLE_MIXER
;
208 c
->volume
= mpd_status_get_volume(c
->status
);
210 /* check if the playlist needs an update */
211 if (c
->playlist
.version
!= mpd_status_get_queue_version(c
->status
)) {
214 if (c
->source
== NULL
)
215 c
->events
|= MPD_IDLE_QUEUE
;
217 if (!playlist_is_empty(&c
->playlist
))
218 retval
= mpdclient_playlist_update_changes(c
);
220 retval
= mpdclient_playlist_update(c
);
225 /* update the current song */
226 if (!c
->song
|| mpd_status_get_song_id(c
->status
) >= 0) {
227 c
->song
= playlist_get_song(&c
->playlist
,
228 mpd_status_get_song_pos(c
->status
));
234 struct mpd_connection
*
235 mpdclient_get_connection(struct mpdclient
*c
)
237 if (c
->source
!= NULL
&& c
->idle
) {
239 mpd_glib_leave(c
->source
);
242 return c
->connection
;
246 mpdclient_put_connection(struct mpdclient
*c
)
248 assert(c
->source
== NULL
|| c
->connection
!= NULL
);
250 if (c
->source
!= NULL
&& !c
->idle
) {
251 c
->idle
= mpd_glib_enter(c
->source
);
255 static struct mpd_status
*
256 mpdclient_recv_status(struct mpdclient
*c
)
258 assert(c
->connection
!= NULL
);
260 struct mpd_status
*status
= mpd_recv_status(c
->connection
);
261 if (status
== NULL
) {
262 mpdclient_handle_error(c
);
266 if (c
->status
!= NULL
)
267 mpd_status_free(c
->status
);
268 return c
->status
= status
;
271 /****************************************************************************/
272 /*** MPD Commands **********************************************************/
273 /****************************************************************************/
276 mpdclient_cmd_crop(struct mpdclient
*c
)
278 if (!mpdclient_is_playing(c
))
281 int length
= mpd_status_get_queue_length(c
->status
);
282 int current
= mpd_status_get_song_pos(c
->status
);
283 if (current
< 0 || mpd_status_get_queue_length(c
->status
) < 2)
286 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
287 if (connection
== NULL
)
290 mpd_command_list_begin(connection
, false);
292 if (current
< length
- 1)
293 mpd_send_delete_range(connection
, current
+ 1, length
);
295 mpd_send_delete_range(connection
, 0, current
);
297 mpd_command_list_end(connection
);
299 return mpdclient_finish_command(c
);
303 mpdclient_cmd_clear(struct mpdclient
*c
)
305 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
306 if (connection
== NULL
)
309 /* send "clear" and "status" */
310 if (!mpd_command_list_begin(connection
, false) ||
311 !mpd_send_clear(connection
) ||
312 !mpd_send_status(connection
) ||
313 !mpd_command_list_end(connection
))
314 return mpdclient_handle_error(c
);
316 /* receive the new status, store it in the mpdclient struct */
318 struct mpd_status
*status
= mpdclient_recv_status(c
);
322 if (!mpd_response_finish(connection
))
323 return mpdclient_handle_error(c
);
325 /* update mpdclient.playlist */
327 if (mpd_status_get_queue_length(status
) == 0) {
328 /* after the "clear" command, the queue is really
329 empty - this means we can clear it locally,
330 reducing the UI latency */
331 playlist_clear(&c
->playlist
);
332 c
->playlist
.version
= mpd_status_get_queue_version(status
);
336 c
->events
|= MPD_IDLE_QUEUE
;
341 mpdclient_cmd_volume(struct mpdclient
*c
, gint value
)
343 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
344 if (connection
== NULL
)
347 mpd_send_set_volume(connection
, value
);
348 return mpdclient_finish_command(c
);
352 mpdclient_cmd_volume_up(struct mpdclient
*c
)
354 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
355 if (connection
== NULL
)
358 if (c
->status
== NULL
||
359 mpd_status_get_volume(c
->status
) == -1)
363 c
->volume
= mpd_status_get_volume(c
->status
);
365 if (c
->volume
>= 100)
368 return mpdclient_cmd_volume(c
, ++c
->volume
);
372 mpdclient_cmd_volume_down(struct mpdclient
*c
)
374 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
375 if (connection
== NULL
)
378 if (c
->status
== NULL
|| mpd_status_get_volume(c
->status
) < 0)
382 c
->volume
= mpd_status_get_volume(c
->status
);
387 return mpdclient_cmd_volume(c
, --c
->volume
);
391 mpdclient_cmd_add_path(struct mpdclient
*c
, const gchar
*path_utf8
)
393 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
394 if (connection
== NULL
)
397 return mpd_send_add(connection
, path_utf8
)?
398 mpdclient_finish_command(c
) : false;
402 mpdclient_cmd_add(struct mpdclient
*c
, const struct mpd_song
*song
)
405 assert(song
!= NULL
);
407 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
408 if (connection
== NULL
|| c
->status
== NULL
)
411 /* send the add command to mpd; at the same time, get the new
412 status (to verify the new playlist id) and the last song
413 (we hope that's the song we just added) */
415 if (!mpd_command_list_begin(connection
, true) ||
416 !mpd_send_add(connection
, mpd_song_get_uri(song
)) ||
417 !mpd_send_status(connection
) ||
418 !mpd_send_get_queue_song_pos(connection
,
419 playlist_length(&c
->playlist
)) ||
420 !mpd_command_list_end(connection
) ||
421 !mpd_response_next(connection
))
422 return mpdclient_handle_error(c
);
424 c
->events
|= MPD_IDLE_QUEUE
;
426 struct mpd_status
*status
= mpdclient_recv_status(c
);
430 if (!mpd_response_next(connection
))
431 return mpdclient_handle_error(c
);
433 struct mpd_song
*new_song
= mpd_recv_song(connection
);
434 if (!mpd_response_finish(connection
) || new_song
== NULL
) {
435 if (new_song
!= NULL
)
436 mpd_song_free(new_song
);
438 return mpd_connection_clear_error(connection
) ||
439 mpdclient_handle_error(c
);
442 if (mpd_status_get_queue_length(status
) == playlist_length(&c
->playlist
) + 1 &&
443 mpd_status_get_queue_version(status
) == c
->playlist
.version
+ 1) {
444 /* the cheap route: match on the new playlist length
445 and its version, we can keep our local playlist
447 c
->playlist
.version
= mpd_status_get_queue_version(status
);
449 /* the song we just received has the correct id;
450 append it to the local playlist */
451 playlist_append(&c
->playlist
, new_song
);
454 mpd_song_free(new_song
);
460 mpdclient_cmd_delete(struct mpdclient
*c
, gint idx
)
462 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
464 if (connection
== NULL
|| c
->status
== NULL
)
467 if (idx
< 0 || (guint
)idx
>= playlist_length(&c
->playlist
))
470 const struct mpd_song
*song
= playlist_get(&c
->playlist
, idx
);
472 /* send the delete command to mpd; at the same time, get the
473 new status (to verify the playlist id) */
475 if (!mpd_command_list_begin(connection
, false) ||
476 !mpd_send_delete_id(connection
, mpd_song_get_id(song
)) ||
477 !mpd_send_status(connection
) ||
478 !mpd_command_list_end(connection
))
479 return mpdclient_handle_error(c
);
481 c
->events
|= MPD_IDLE_QUEUE
;
483 struct mpd_status
*status
= mpdclient_recv_status(c
);
487 if (!mpd_response_finish(connection
))
488 return mpdclient_handle_error(c
);
490 if (mpd_status_get_queue_length(status
) == playlist_length(&c
->playlist
) - 1 &&
491 mpd_status_get_queue_version(status
) == c
->playlist
.version
+ 1) {
492 /* the cheap route: match on the new playlist length
493 and its version, we can keep our local playlist
495 c
->playlist
.version
= mpd_status_get_queue_version(status
);
497 /* remove the song from the local playlist */
498 playlist_remove(&c
->playlist
, idx
);
500 /* remove references to the song */
509 mpdclient_cmd_delete_range(struct mpdclient
*c
, unsigned start
, unsigned end
)
511 if (end
== start
+ 1)
512 /* if that's not really a range, we choose to use the
513 safer "deleteid" version */
514 return mpdclient_cmd_delete(c
, start
);
516 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
517 if (connection
== NULL
)
520 /* send the delete command to mpd; at the same time, get the
521 new status (to verify the playlist id) */
523 if (!mpd_command_list_begin(connection
, false) ||
524 !mpd_send_delete_range(connection
, start
, end
) ||
525 !mpd_send_status(connection
) ||
526 !mpd_command_list_end(connection
))
527 return mpdclient_handle_error(c
);
529 c
->events
|= MPD_IDLE_QUEUE
;
531 struct mpd_status
*status
= mpdclient_recv_status(c
);
535 if (!mpd_response_finish(connection
))
536 return mpdclient_handle_error(c
);
538 if (mpd_status_get_queue_length(status
) == playlist_length(&c
->playlist
) - (end
- start
) &&
539 mpd_status_get_queue_version(status
) == c
->playlist
.version
+ 1) {
540 /* the cheap route: match on the new playlist length
541 and its version, we can keep our local playlist
543 c
->playlist
.version
= mpd_status_get_queue_version(status
);
545 /* remove the song from the local playlist */
546 while (end
> start
) {
549 /* remove references to the song */
550 if (c
->song
== playlist_get(&c
->playlist
, end
))
553 playlist_remove(&c
->playlist
, end
);
561 mpdclient_cmd_move(struct mpdclient
*c
, unsigned dest_pos
, unsigned src_pos
)
563 if (dest_pos
== src_pos
)
566 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
567 if (connection
== NULL
)
570 /* send the "move" command to MPD; at the same time, get the
571 new status (to verify the playlist id) */
573 if (!mpd_command_list_begin(connection
, false) ||
574 !mpd_send_move(connection
, src_pos
, dest_pos
) ||
575 !mpd_send_status(connection
) ||
576 !mpd_command_list_end(connection
))
577 return mpdclient_handle_error(c
);
579 c
->events
|= MPD_IDLE_QUEUE
;
581 struct mpd_status
*status
= mpdclient_recv_status(c
);
585 if (!mpd_response_finish(connection
))
586 return mpdclient_handle_error(c
);
588 if (mpd_status_get_queue_length(status
) == playlist_length(&c
->playlist
) &&
589 mpd_status_get_queue_version(status
) == c
->playlist
.version
+ 1) {
590 /* the cheap route: match on the new playlist length
591 and its version, we can keep our local playlist
593 c
->playlist
.version
= mpd_status_get_queue_version(status
);
595 /* swap songs in the local playlist */
596 playlist_move(&c
->playlist
, dest_pos
, src_pos
);
602 #if LIBMPDCLIENT_CHECK_VERSION(2,5,0)
603 /* The client-to-client protocol (MPD 0.17.0) */
606 mpdclient_cmd_subscribe(struct mpdclient
*c
, const char *channel
)
608 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
610 if (connection
== NULL
)
613 if (!mpd_send_subscribe(connection
, channel
))
614 return mpdclient_handle_error(c
);
616 return mpdclient_finish_command(c
);
620 mpdclient_cmd_unsubscribe(struct mpdclient
*c
, const char *channel
)
622 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
623 if (connection
== NULL
)
626 if (!mpd_send_unsubscribe(connection
, channel
))
627 return mpdclient_handle_error(c
);
629 return mpdclient_finish_command(c
);
633 mpdclient_cmd_send_message(struct mpdclient
*c
, const char *channel
,
636 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
637 if (connection
== NULL
)
640 if (!mpd_send_send_message(connection
, channel
, text
))
641 return mpdclient_handle_error(c
);
643 return mpdclient_finish_command(c
);
647 mpdclient_send_read_messages(struct mpdclient
*c
)
649 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
650 if (connection
== NULL
)
653 return mpd_send_read_messages(connection
)?
654 true : mpdclient_handle_error(c
);
658 mpdclient_recv_message(struct mpdclient
*c
)
660 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
661 if (connection
== NULL
)
664 struct mpd_message
*message
= mpd_recv_message(connection
);
665 if (message
== NULL
&&
666 mpd_connection_get_error(connection
) != MPD_ERROR_SUCCESS
)
667 mpdclient_handle_error(c
);
673 /****************************************************************************/
674 /*** Playlist management functions ******************************************/
675 /****************************************************************************/
677 /* update playlist */
679 mpdclient_playlist_update(struct mpdclient
*c
)
681 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
682 if (connection
== NULL
)
685 playlist_clear(&c
->playlist
);
687 mpd_send_list_queue_meta(connection
);
689 struct mpd_entity
*entity
;
690 while ((entity
= mpd_recv_entity(connection
))) {
691 if (mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
)
692 playlist_append(&c
->playlist
, mpd_entity_get_song(entity
));
694 mpd_entity_free(entity
);
697 c
->playlist
.version
= mpd_status_get_queue_version(c
->status
);
700 return mpdclient_finish_command(c
);
703 /* update playlist (plchanges) */
705 mpdclient_playlist_update_changes(struct mpdclient
*c
)
707 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
709 if (connection
== NULL
)
712 mpd_send_queue_changes_meta(connection
, c
->playlist
.version
);
714 struct mpd_song
*song
;
715 while ((song
= mpd_recv_song(connection
)) != NULL
) {
716 int pos
= mpd_song_get_pos(song
);
718 if (pos
>= 0 && (guint
)pos
< c
->playlist
.list
->len
) {
720 playlist_replace(&c
->playlist
, pos
, song
);
723 playlist_append(&c
->playlist
, song
);
729 /* remove trailing songs */
731 unsigned length
= mpd_status_get_queue_length(c
->status
);
732 while (length
< c
->playlist
.list
->len
) {
733 guint pos
= c
->playlist
.list
->len
- 1;
735 /* Remove the last playlist entry */
736 playlist_remove(&c
->playlist
, pos
);
740 c
->playlist
.version
= mpd_status_get_queue_version(c
->status
);
742 return mpdclient_finish_command(c
);
746 /****************************************************************************/
747 /*** Filelist functions *****************************************************/
748 /****************************************************************************/
751 mpdclient_filelist_add_all(struct mpdclient
*c
, struct filelist
*fl
)
753 struct mpd_connection
*connection
= mpdclient_get_connection(c
);
754 if (connection
== NULL
)
757 if (filelist_is_empty(fl
))
760 mpd_command_list_begin(connection
, false);
762 for (unsigned i
= 0; i
< filelist_length(fl
); ++i
) {
763 struct filelist_entry
*entry
= filelist_get(fl
, i
);
764 struct mpd_entity
*entity
= entry
->entity
;
766 if (entity
!= NULL
&&
767 mpd_entity_get_type(entity
) == MPD_ENTITY_TYPE_SONG
) {
768 const struct mpd_song
*song
=
769 mpd_entity_get_song(entity
);
771 mpd_send_add(connection
, mpd_song_get_uri(song
));
775 mpd_command_list_end(connection
);
776 return mpdclient_finish_command(c
);