lyrics: lyrics area regex fix for LyricWikia
[ncmpc.git] / src / mpdclient.c
blob1e01486a27c8c65bffcfaead13f95a08612a7eaa
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"
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"
27 #include "gidle.h"
29 #include <mpd/client.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <time.h>
34 #include <string.h>
36 #define BUFSIZE 1024
38 /* sort by song format */
39 gint
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;
49 int n = 0;
50 if (e1 && e2 &&
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);
59 return n;
63 /****************************************************************************/
64 /*** mpdclient functions ****************************************************/
65 /****************************************************************************/
67 bool
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 &&
76 screen_auth(c))
77 return true;
79 mpdclient_ui_error(mpd_connection_get_error_message(c->connection));
81 if (!mpd_connection_clear_error(c->connection))
82 mpdclient_disconnect(c);
84 return false;
87 struct mpdclient *
88 mpdclient_new(void)
90 struct mpdclient *c = g_new0(struct mpdclient, 1);
91 playlist_init(&c->playlist);
92 c->volume = -1;
93 c->events = 0;
95 return c;
98 void
99 mpdclient_free(struct mpdclient *c)
101 mpdclient_disconnect(c);
103 mpdclient_playlist_free(&c->playlist);
105 g_free(c);
108 void
109 mpdclient_disconnect(struct mpdclient *c)
111 if (c->source != NULL) {
112 mpd_glib_free(c->source);
113 c->source = NULL;
114 c->idle = false;
117 if (c->connection) {
118 mpd_connection_free(c->connection);
119 ++c->connection_id;
121 c->connection = NULL;
123 if (c->status)
124 mpd_status_free(c->status);
125 c->status = NULL;
127 playlist_clear(&c->playlist);
129 if (c->song)
130 c->song = NULL;
132 /* everything has changed after a disconnect */
133 c->events |= MPD_IDLE_ALL;
136 bool
137 mpdclient_connect(struct mpdclient *c,
138 const gchar *host,
139 gint port,
140 unsigned timeout_ms,
141 const gchar *password)
143 /* close any open connection */
144 if (c->connection)
145 mpdclient_disconnect(c);
147 /* connect to MPD */
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);
155 return false;
158 /* send password */
159 if (password != NULL && !mpd_run_password(c->connection, password)) {
160 mpdclient_handle_error(c);
161 mpdclient_disconnect(c);
162 return false;
165 ++c->connection_id;
167 return true;
170 bool
171 mpdclient_update(struct mpdclient *c)
173 struct mpd_connection *connection = mpdclient_get_connection(c);
175 c->volume = -1;
177 if (connection == NULL)
178 return false;
180 /* always announce these options as long as we don't have
181 "idle" support */
182 if (c->source == NULL)
183 c->events |= MPD_IDLE_PLAYER|MPD_IDLE_OPTIONS;
185 /* free the old status */
186 if (c->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)) {
212 bool retval;
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);
219 else
220 retval = mpdclient_playlist_update(c);
221 if (!retval)
222 return false;
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));
231 return true;
234 struct mpd_connection *
235 mpdclient_get_connection(struct mpdclient *c)
237 if (c->source != NULL && c->idle) {
238 c->idle = false;
239 mpd_glib_leave(c->source);
242 return c->connection;
245 void
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);
263 return NULL;
266 if (c->status != NULL)
267 mpd_status_free(c->status);
268 return c->status = status;
271 /****************************************************************************/
272 /*** MPD Commands **********************************************************/
273 /****************************************************************************/
275 bool
276 mpdclient_cmd_crop(struct mpdclient *c)
278 if (!mpdclient_is_playing(c))
279 return false;
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)
284 return true;
286 struct mpd_connection *connection = mpdclient_get_connection(c);
287 if (connection == NULL)
288 return false;
290 mpd_command_list_begin(connection, false);
292 if (current < length - 1)
293 mpd_send_delete_range(connection, current + 1, length);
294 if (current > 0)
295 mpd_send_delete_range(connection, 0, current);
297 mpd_command_list_end(connection);
299 return mpdclient_finish_command(c);
302 bool
303 mpdclient_cmd_clear(struct mpdclient *c)
305 struct mpd_connection *connection = mpdclient_get_connection(c);
306 if (connection == NULL)
307 return false;
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);
319 if (status == NULL)
320 return false;
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);
333 c->song = NULL;
336 c->events |= MPD_IDLE_QUEUE;
337 return true;
340 bool
341 mpdclient_cmd_volume(struct mpdclient *c, gint value)
343 struct mpd_connection *connection = mpdclient_get_connection(c);
344 if (connection == NULL)
345 return false;
347 mpd_send_set_volume(connection, value);
348 return mpdclient_finish_command(c);
351 bool
352 mpdclient_cmd_volume_up(struct mpdclient *c)
354 struct mpd_connection *connection = mpdclient_get_connection(c);
355 if (connection == NULL)
356 return false;
358 if (c->status == NULL ||
359 mpd_status_get_volume(c->status) == -1)
360 return true;
362 if (c->volume < 0)
363 c->volume = mpd_status_get_volume(c->status);
365 if (c->volume >= 100)
366 return true;
368 return mpdclient_cmd_volume(c, ++c->volume);
371 bool
372 mpdclient_cmd_volume_down(struct mpdclient *c)
374 struct mpd_connection *connection = mpdclient_get_connection(c);
375 if (connection == NULL)
376 return false;
378 if (c->status == NULL || mpd_status_get_volume(c->status) < 0)
379 return true;
381 if (c->volume < 0)
382 c->volume = mpd_status_get_volume(c->status);
384 if (c->volume <= 0)
385 return true;
387 return mpdclient_cmd_volume(c, --c->volume);
390 bool
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)
395 return false;
397 return mpd_send_add(connection, path_utf8)?
398 mpdclient_finish_command(c) : false;
401 bool
402 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
404 assert(c != NULL);
405 assert(song != NULL);
407 struct mpd_connection *connection = mpdclient_get_connection(c);
408 if (connection == NULL || c->status == NULL)
409 return false;
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);
427 if (status == NULL)
428 return false;
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
446 copy in sync */
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);
456 return true;
459 bool
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)
465 return false;
467 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
468 return false;
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);
484 if (status == NULL)
485 return false;
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
494 copy in sync */
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 */
501 if (c->song == song)
502 c->song = NULL;
505 return true;
508 bool
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)
518 return false;
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);
532 if (status == NULL)
533 return false;
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
542 copy in sync */
543 c->playlist.version = mpd_status_get_queue_version(status);
545 /* remove the song from the local playlist */
546 while (end > start) {
547 --end;
549 /* remove references to the song */
550 if (c->song == playlist_get(&c->playlist, end))
551 c->song = NULL;
553 playlist_remove(&c->playlist, end);
557 return true;
560 bool
561 mpdclient_cmd_move(struct mpdclient *c, unsigned dest_pos, unsigned src_pos)
563 if (dest_pos == src_pos)
564 return true;
566 struct mpd_connection *connection = mpdclient_get_connection(c);
567 if (connection == NULL)
568 return false;
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);
582 if (status == NULL)
583 return false;
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
592 copy in sync */
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);
599 return true;
602 #if LIBMPDCLIENT_CHECK_VERSION(2,5,0)
603 /* The client-to-client protocol (MPD 0.17.0) */
605 bool
606 mpdclient_cmd_subscribe(struct mpdclient *c, const char *channel)
608 struct mpd_connection *connection = mpdclient_get_connection(c);
610 if (connection == NULL)
611 return false;
613 if (!mpd_send_subscribe(connection, channel))
614 return mpdclient_handle_error(c);
616 return mpdclient_finish_command(c);
619 bool
620 mpdclient_cmd_unsubscribe(struct mpdclient *c, const char *channel)
622 struct mpd_connection *connection = mpdclient_get_connection(c);
623 if (connection == NULL)
624 return false;
626 if (!mpd_send_unsubscribe(connection, channel))
627 return mpdclient_handle_error(c);
629 return mpdclient_finish_command(c);
632 bool
633 mpdclient_cmd_send_message(struct mpdclient *c, const char *channel,
634 const char *text)
636 struct mpd_connection *connection = mpdclient_get_connection(c);
637 if (connection == NULL)
638 return false;
640 if (!mpd_send_send_message(connection, channel, text))
641 return mpdclient_handle_error(c);
643 return mpdclient_finish_command(c);
646 bool
647 mpdclient_send_read_messages(struct mpdclient *c)
649 struct mpd_connection *connection = mpdclient_get_connection(c);
650 if (connection == NULL)
651 return false;
653 return mpd_send_read_messages(connection)?
654 true : mpdclient_handle_error(c);
657 struct mpd_message *
658 mpdclient_recv_message(struct mpdclient *c)
660 struct mpd_connection *connection = mpdclient_get_connection(c);
661 if (connection == NULL)
662 return false;
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);
669 return message;
671 #endif
673 /****************************************************************************/
674 /*** Playlist management functions ******************************************/
675 /****************************************************************************/
677 /* update playlist */
678 bool
679 mpdclient_playlist_update(struct mpdclient *c)
681 struct mpd_connection *connection = mpdclient_get_connection(c);
682 if (connection == NULL)
683 return false;
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);
698 c->song = NULL;
700 return mpdclient_finish_command(c);
703 /* update playlist (plchanges) */
704 bool
705 mpdclient_playlist_update_changes(struct mpdclient *c)
707 struct mpd_connection *connection = mpdclient_get_connection(c);
709 if (connection == NULL)
710 return false;
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) {
719 /* update song */
720 playlist_replace(&c->playlist, pos, song);
721 } else {
722 /* add a new song */
723 playlist_append(&c->playlist, song);
726 mpd_song_free(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);
739 c->song = NULL;
740 c->playlist.version = mpd_status_get_queue_version(c->status);
742 return mpdclient_finish_command(c);
746 /****************************************************************************/
747 /*** Filelist functions *****************************************************/
748 /****************************************************************************/
750 bool
751 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
753 struct mpd_connection *connection = mpdclient_get_connection(c);
754 if (connection == NULL)
755 return false;
757 if (filelist_is_empty(fl))
758 return true;
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);