Code

54ce2c7145b7dc4b95a7a277a54874337108f8eb
[ncmpc.git] / src / mpdclient.c
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 #define BUFSIZE 1024
37 /* sort by list-format */
38 gint
39 compare_filelistentry_format(gconstpointer filelist_entry1,
40                              gconstpointer filelist_entry2)
41 {
42         const struct mpd_entity *e1, *e2;
43         char key1[BUFSIZE], key2[BUFSIZE];
44         int n = 0;
46         e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
47         e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
49         if (e1 && e2 &&
50             mpd_entity_get_type(e1) == MPD_ENTITY_TYPE_SONG &&
51             mpd_entity_get_type(e2) == MPD_ENTITY_TYPE_SONG) {
52                 strfsong(key1, BUFSIZE, options.list_format, mpd_entity_get_song(e1));
53                 strfsong(key2, BUFSIZE, options.list_format, mpd_entity_get_song(e2));
54                 n = strcmp(key1,key2);
55         }
57         return n;
58 }
61 /****************************************************************************/
62 /*** mpdclient functions ****************************************************/
63 /****************************************************************************/
65 bool
66 mpdclient_handle_error(struct mpdclient *c)
67 {
68         enum mpd_error error = mpd_connection_get_error(c->connection);
70         assert(error != MPD_ERROR_SUCCESS);
72         if (error == MPD_ERROR_SERVER &&
73             mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION &&
74             screen_auth(c))
75                 return true;
77         mpdclient_ui_error(mpd_connection_get_error_message(c->connection));
79         if (!mpd_connection_clear_error(c->connection))
80                 mpdclient_disconnect(c);
82         return false;
83 }
85 static bool
86 mpdclient_finish_command(struct mpdclient *c)
87 {
88         return mpd_response_finish(c->connection)
89                 ? true : mpdclient_handle_error(c);
90 }
92 struct mpdclient *
93 mpdclient_new(void)
94 {
95         struct mpdclient *c;
97         c = g_new0(struct mpdclient, 1);
98         playlist_init(&c->playlist);
99         c->volume = -1;
100         c->events = 0;
102         return c;
105 void
106 mpdclient_free(struct mpdclient *c)
108         mpdclient_disconnect(c);
110         mpdclient_playlist_free(&c->playlist);
112         g_free(c);
115 void
116 mpdclient_disconnect(struct mpdclient *c)
118         if (c->connection)
119                 mpd_connection_free(c->connection);
120         c->connection = NULL;
122         if (c->status)
123                 mpd_status_free(c->status);
124         c->status = NULL;
126         playlist_clear(&c->playlist);
128         if (c->song)
129                 c->song = NULL;
132 bool
133 mpdclient_connect(struct mpdclient *c,
134                   const gchar *host,
135                   gint port,
136                   gfloat _timeout,
137                   const gchar *password)
139         /* close any open connection */
140         if( c->connection )
141                 mpdclient_disconnect(c);
143         /* connect to MPD */
144         c->connection = mpd_connection_new(host, port, _timeout * 1000);
145         if (c->connection == NULL)
146                 g_error("Out of memory");
148         if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
149                 mpdclient_handle_error(c);
150                 mpdclient_disconnect(c);
151                 return false;
152         }
154         /* send password */
155         if (password != NULL && !mpd_run_password(c->connection, password)) {
156                 mpdclient_handle_error(c);
157                 mpdclient_disconnect(c);
158                 return false;
159         }
161         return true;
164 bool
165 mpdclient_update(struct mpdclient *c)
167         struct mpd_connection *connection = mpdclient_get_connection(c);
168         bool retval;
170         c->volume = -1;
172         if (connection == NULL)
173                 return false;
175         /* always announce these options as long as we don't have real
176            "idle" support */
177         c->events |= MPD_IDLE_PLAYER|MPD_IDLE_OPTIONS;
179         /* free the old status */
180         if (c->status)
181                 mpd_status_free(c->status);
183         /* retrieve new status */
184         c->status = mpd_run_status(connection);
185         if (c->status == NULL)
186                 return mpdclient_handle_error(c);
188         if (c->update_id != mpd_status_get_update_id(c->status)) {
189                 c->events |= MPD_IDLE_UPDATE;
191                 if (c->update_id > 0)
192                         c->events |= MPD_IDLE_DATABASE;
193         }
195         c->update_id = mpd_status_get_update_id(c->status);
197         if (c->volume != mpd_status_get_volume(c->status))
198                 c->events |= MPD_IDLE_MIXER;
200         c->volume = mpd_status_get_volume(c->status);
202         /* check if the playlist needs an update */
203         if (c->playlist.version != mpd_status_get_queue_version(c->status)) {
204                 c->events |= MPD_IDLE_PLAYLIST;
206                 if (!playlist_is_empty(&c->playlist))
207                         retval = mpdclient_playlist_update_changes(c);
208                 else
209                         retval = mpdclient_playlist_update(c);
210         } else
211                 retval = true;
213         /* update the current song */
214         if (!c->song || mpd_status_get_song_id(c->status)) {
215                 c->song = playlist_get_song(&c->playlist,
216                                             mpd_status_get_song_pos(c->status));
217         }
219         return retval;
222 struct mpd_connection *
223 mpdclient_get_connection(struct mpdclient *c)
225         return c->connection;
228 void
229 mpdclient_put_connection(G_GNUC_UNUSED struct mpdclient *c)
234 /****************************************************************************/
235 /*** MPD Commands  **********************************************************/
236 /****************************************************************************/
238 bool
239 mpdclient_cmd_play(struct mpdclient *c, gint idx)
241         struct mpd_connection *connection = mpdclient_get_connection(c);
242         const struct mpd_song *song = playlist_get_song(&c->playlist, idx);
244         if (connection == NULL)
245                 return false;
247         if (song)
248                 mpd_send_play_id(connection, mpd_song_get_id(song));
249         else
250                 mpd_send_play(connection);
252         return mpdclient_finish_command(c);
255 bool
256 mpdclient_cmd_crop(struct mpdclient *c)
258         struct mpd_connection *connection = mpdclient_get_connection(c);
259         struct mpd_status *status;
260         bool playing;
261         int length, current;
263         if (connection == NULL)
264                 return false;
266         status = mpd_run_status(connection);
267         if (status == NULL)
268                 return mpdclient_handle_error(c);
270         playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
271                 mpd_status_get_state(status) == MPD_STATE_PAUSE;
272         length = mpd_status_get_queue_length(status);
273         current = mpd_status_get_song_pos(status);
275         mpd_status_free(status);
277         if (!playing || length < 2)
278                 return true;
280         mpd_command_list_begin(connection, false);
282         while (--length >= 0)
283                 if (length != current)
284                         mpd_send_delete(connection, length);
286         mpd_command_list_end(connection);
288         return mpdclient_finish_command(c);
291 bool
292 mpdclient_cmd_clear(struct mpdclient *c)
294         struct mpd_connection *connection = mpdclient_get_connection(c);
295         bool retval;
297         if (connection == NULL)
298                 return false;
300         mpd_send_clear(connection);
301         retval = mpdclient_finish_command(c);
303         if (retval)
304                 c->events |= MPD_IDLE_PLAYLIST;
306         return retval;
309 bool
310 mpdclient_cmd_volume(struct mpdclient *c, gint value)
312         struct mpd_connection *connection = mpdclient_get_connection(c);
313         if (connection == NULL)
314                 return false;
316         mpd_send_set_volume(connection, value);
317         return mpdclient_finish_command(c);
320 bool
321 mpdclient_cmd_volume_up(struct mpdclient *c)
323         struct mpd_connection *connection = mpdclient_get_connection(c);
324         if (connection == NULL)
325                 return false;
327         if (c->status == NULL ||
328             mpd_status_get_volume(c->status) == -1)
329                 return true;
331         if (c->volume < 0)
332                 c->volume = mpd_status_get_volume(c->status);
334         if (c->volume >= 100)
335                 return true;
337         return mpdclient_cmd_volume(c, ++c->volume);
340 bool
341 mpdclient_cmd_volume_down(struct mpdclient *c)
343         struct mpd_connection *connection = mpdclient_get_connection(c);
344         if (connection == NULL)
345                 return false;
347         if (c->status == NULL || mpd_status_get_volume(c->status) < 0)
348                 return true;
350         if (c->volume < 0)
351                 c->volume = mpd_status_get_volume(c->status);
353         if (c->volume <= 0)
354                 return true;
356         return mpdclient_cmd_volume(c, --c->volume);
359 bool
360 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
362         struct mpd_connection *connection = mpdclient_get_connection(c);
363         if (connection == NULL)
364                 return false;
366         mpd_send_add(connection, path_utf8);
367         return mpdclient_finish_command(c);
370 bool
371 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
373         struct mpd_connection *connection = mpdclient_get_connection(c);
374         struct mpd_status *status;
375         struct mpd_song *new_song;
377         assert(c != NULL);
378         assert(song != NULL);
380         if (connection == NULL || c->status == NULL)
381                 return false;
383         /* send the add command to mpd; at the same time, get the new
384            status (to verify the new playlist id) and the last song
385            (we hope that's the song we just added) */
387         if (!mpd_command_list_begin(connection, true) ||
388             !mpd_send_add(connection, mpd_song_get_uri(song)) ||
389             !mpd_send_status(connection) ||
390             !mpd_send_get_queue_song_pos(connection,
391                                          playlist_length(&c->playlist)) ||
392             !mpd_command_list_end(connection) ||
393             !mpd_response_next(connection))
394                 return mpdclient_handle_error(c);
396         c->events |= MPD_IDLE_PLAYLIST;
398         status = mpd_recv_status(connection);
399         if (status != NULL) {
400                 if (c->status != NULL)
401                         mpd_status_free(c->status);
402                 c->status = status;
403         }
405         if (!mpd_response_next(connection))
406                 return mpdclient_handle_error(c);
408         new_song = mpd_recv_song(connection);
409         if (!mpd_response_finish(connection) || new_song == NULL) {
410                 if (new_song != NULL)
411                         mpd_song_free(new_song);
413                 return mpd_connection_clear_error(connection) ||
414                         mpdclient_handle_error(c);
415         }
417         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) + 1 &&
418             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
419                 /* the cheap route: match on the new playlist length
420                    and its version, we can keep our local playlist
421                    copy in sync */
422                 c->playlist.version = mpd_status_get_queue_version(status);
424                 /* the song we just received has the correct id;
425                    append it to the local playlist */
426                 playlist_append(&c->playlist, new_song);
427         }
429         mpd_song_free(new_song);
431         return true;
434 bool
435 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
437         struct mpd_connection *connection = mpdclient_get_connection(c);
438         const struct mpd_song *song;
439         struct mpd_status *status;
441         if (connection == NULL || c->status == NULL)
442                 return false;
444         if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
445                 return false;
447         song = playlist_get(&c->playlist, idx);
449         /* send the delete command to mpd; at the same time, get the
450            new status (to verify the playlist id) */
452         if (!mpd_command_list_begin(connection, false) ||
453             !mpd_send_delete_id(connection, mpd_song_get_id(song)) ||
454             !mpd_send_status(connection) ||
455             !mpd_command_list_end(connection))
456                 return mpdclient_handle_error(c);
458         c->events |= MPD_IDLE_PLAYLIST;
460         status = mpd_recv_status(connection);
461         if (status != NULL) {
462                 if (c->status != NULL)
463                         mpd_status_free(c->status);
464                 c->status = status;
465         }
467         if (!mpd_response_finish(connection))
468                 return mpdclient_handle_error(c);
470         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - 1 &&
471             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
472                 /* the cheap route: match on the new playlist length
473                    and its version, we can keep our local playlist
474                    copy in sync */
475                 c->playlist.version = mpd_status_get_queue_version(status);
477                 /* remove the song from the local playlist */
478                 playlist_remove(&c->playlist, idx);
480                 /* remove references to the song */
481                 if (c->song == song)
482                         c->song = NULL;
483         }
485         return true;
488 /**
489  * Fallback for mpdclient_cmd_delete_range() on MPD older than 0.16.
490  * It emulates the "delete range" command with a list of simple
491  * "delete" commands.
492  */
493 static bool
494 mpdclient_cmd_delete_range_fallback(struct mpdclient *c,
495                                     unsigned start, unsigned end)
497         struct mpd_connection *connection = mpdclient_get_connection(c);
498         if (connection == NULL)
499                 return false;
501         if (!mpd_command_list_begin(connection, false))
502                 return mpdclient_handle_error(c);
504         for (; start < end; --end)
505                 mpd_send_delete(connection, start);
507         if (!mpd_command_list_end(connection) ||
508             !mpd_response_finish(connection))
509                 return mpdclient_handle_error(c);
511         return true;
514 bool
515 mpdclient_cmd_delete_range(struct mpdclient *c, unsigned start, unsigned end)
517         struct mpd_connection *connection;
518         struct mpd_status *status;
520         connection = mpdclient_get_connection(c);
521         if (connection == NULL)
522                 return false;
524         if (mpd_connection_cmp_server_version(connection, 0, 16, 0) < 0)
525                 return mpdclient_cmd_delete_range_fallback(c, start, end);
527         /* MPD 0.16 supports "delete" with a range argument */
529         /* send the delete command to mpd; at the same time, get the
530            new status (to verify the playlist id) */
532         if (!mpd_command_list_begin(connection, false) ||
533             !mpd_send_delete_range(connection, start, end) ||
534             !mpd_send_status(connection) ||
535             !mpd_command_list_end(connection))
536                 return mpdclient_handle_error(c);
538         c->events |= MPD_IDLE_PLAYLIST;
540         status = mpd_recv_status(connection);
541         if (status != NULL) {
542                 if (c->status != NULL)
543                         mpd_status_free(c->status);
544                 c->status = status;
545         }
547         if (!mpd_response_finish(connection))
548                 return mpdclient_handle_error(c);
550         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - (end - start) &&
551             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
552                 /* the cheap route: match on the new playlist length
553                    and its version, we can keep our local playlist
554                    copy in sync */
555                 c->playlist.version = mpd_status_get_queue_version(status);
557                 /* remove the song from the local playlist */
558                 while (end > start) {
559                         --end;
561                         /* remove references to the song */
562                         if (c->song == playlist_get(&c->playlist, end))
563                                 c->song = NULL;
565                         playlist_remove(&c->playlist, end);
566                 }
567         }
569         return true;
572 bool
573 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
575         struct mpd_connection *connection = mpdclient_get_connection(c);
576         const struct mpd_song *song1, *song2;
577         struct mpd_status *status;
579         if (connection == NULL)
580                 return false;
582         if (old_index == new_index || new_index < 0 ||
583             (guint)new_index >= c->playlist.list->len)
584                 return false;
586         song1 = playlist_get(&c->playlist, old_index);
587         song2 = playlist_get(&c->playlist, new_index);
589         /* send the delete command to mpd; at the same time, get the
590            new status (to verify the playlist id) */
592         if (!mpd_command_list_begin(connection, false) ||
593             !mpd_send_swap_id(connection, mpd_song_get_id(song1),
594                               mpd_song_get_id(song2)) ||
595             !mpd_send_status(connection) ||
596             !mpd_command_list_end(connection))
597                 return mpdclient_handle_error(c);
599         c->events |= MPD_IDLE_PLAYLIST;
601         status = mpd_recv_status(connection);
602         if (status != NULL) {
603                 if (c->status != NULL)
604                         mpd_status_free(c->status);
605                 c->status = status;
606         }
608         if (!mpd_response_finish(connection))
609                 return mpdclient_handle_error(c);
611         if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) &&
612             mpd_status_get_queue_version(status) == c->playlist.version + 1) {
613                 /* the cheap route: match on the new playlist length
614                    and its version, we can keep our local playlist
615                    copy in sync */
616                 c->playlist.version = mpd_status_get_queue_version(status);
618                 /* swap songs in the local playlist */
619                 playlist_swap(&c->playlist, old_index, new_index);
620         }
622         return true;
626 /****************************************************************************/
627 /*** Playlist management functions ******************************************/
628 /****************************************************************************/
630 /* update playlist */
631 bool
632 mpdclient_playlist_update(struct mpdclient *c)
634         struct mpd_connection *connection = mpdclient_get_connection(c);
635         struct mpd_entity *entity;
637         if (connection == NULL)
638                 return false;
640         playlist_clear(&c->playlist);
642         mpd_send_list_queue_meta(connection);
643         while ((entity = mpd_recv_entity(connection))) {
644                 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
645                         playlist_append(&c->playlist, mpd_entity_get_song(entity));
647                 mpd_entity_free(entity);
648         }
650         c->playlist.version = mpd_status_get_queue_version(c->status);
651         c->song = NULL;
653         return mpdclient_finish_command(c);
656 /* update playlist (plchanges) */
657 bool
658 mpdclient_playlist_update_changes(struct mpdclient *c)
660         struct mpd_connection *connection = mpdclient_get_connection(c);
661         struct mpd_song *song;
662         guint length;
664         if (connection == NULL)
665                 return false;
667         mpd_send_queue_changes_meta(connection, c->playlist.version);
669         while ((song = mpd_recv_song(connection)) != NULL) {
670                 int pos = mpd_song_get_pos(song);
672                 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
673                         /* update song */
674                         playlist_replace(&c->playlist, pos, song);
675                 } else {
676                         /* add a new song */
677                         playlist_append(&c->playlist, song);
678                 }
680                 mpd_song_free(song);
681         }
683         /* remove trailing songs */
685         length = mpd_status_get_queue_length(c->status);
686         while (length < c->playlist.list->len) {
687                 guint pos = c->playlist.list->len - 1;
689                 /* Remove the last playlist entry */
690                 playlist_remove(&c->playlist, pos);
691         }
693         c->song = NULL;
694         c->playlist.version = mpd_status_get_queue_version(c->status);
696         return mpdclient_finish_command(c);
700 /****************************************************************************/
701 /*** Filelist functions *****************************************************/
702 /****************************************************************************/
704 bool
705 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
707         struct mpd_connection *connection = mpdclient_get_connection(c);
708         guint i;
710         if (connection == NULL)
711                 return false;
713         if (filelist_is_empty(fl))
714                 return true;
716         mpd_command_list_begin(connection, false);
718         for (i = 0; i < filelist_length(fl); ++i) {
719                 struct filelist_entry *entry = filelist_get(fl, i);
720                 struct mpd_entity *entity  = entry->entity;
722                 if (entity != NULL &&
723                     mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
724                         const struct mpd_song *song =
725                                 mpd_entity_get_song(entity);
727                         mpd_send_add(connection, mpd_song_get_uri(song));
728                 }
729         }
731         mpd_command_list_end(connection);
732         return mpdclient_finish_command(c);
735 GList *
736 mpdclient_get_artists(struct mpdclient *c)
738         struct mpd_connection *connection = mpdclient_get_connection(c);
739         GList *list = NULL;
740         struct mpd_pair *pair;
742         if (connection == NULL)
743                 return NULL;
745         mpd_search_db_tags(connection, MPD_TAG_ARTIST);
746         mpd_search_commit(connection);
748         while ((pair = mpd_recv_pair_tag(connection,
749                                          MPD_TAG_ARTIST)) != NULL) {
750                 list = g_list_append(list, g_strdup(pair->value));
751                 mpd_return_pair(connection, pair);
752         }
754         if (!mpdclient_finish_command(c))
755                 return string_list_free(list);
757         return list;
760 GList *
761 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
763         struct mpd_connection *connection = mpdclient_get_connection(c);
764         GList *list = NULL;
765         struct mpd_pair *pair;
767         if (connection == NULL)
768                 return NULL;
770         mpd_search_db_tags(connection, MPD_TAG_ALBUM);
771         if (artist_utf8 != NULL)
772                 mpd_search_add_tag_constraint(connection,
773                                               MPD_OPERATOR_DEFAULT,
774                                               MPD_TAG_ARTIST, artist_utf8);
775         mpd_search_commit(connection);
777         while ((pair = mpd_recv_pair_tag(connection,
778                                          MPD_TAG_ALBUM)) != NULL) {
779                 list = g_list_append(list, g_strdup(pair->value));
780                 mpd_return_pair(connection, pair);
781         }
783         if (!mpdclient_finish_command(c))
784                 return string_list_free(list);
786         return list;