Code

mpdclient: check to assertion in mpdclient_cmd_add()
[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 #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)
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         mpdclient_ui_error(mpd_connection_get_error_message(c->connection));
123         if (!mpd_connection_clear_error(c->connection))
124                 mpdclient_disconnect(c);
126         return error;
129 static gint
130 mpdclient_finish_command(struct mpdclient *c)
132         return mpd_response_finish(c->connection)
133                 ? 0 : mpdclient_handle_error(c);
136 struct mpdclient *
137 mpdclient_new(void)
139         struct mpdclient *c;
141         c = g_new0(struct mpdclient, 1);
142         playlist_init(&c->playlist);
143         c->volume = -1;
144         c->events = 0;
146         return c;
149 void
150 mpdclient_free(struct mpdclient *c)
152         mpdclient_disconnect(c);
154         mpdclient_playlist_free(&c->playlist);
156         g_free(c);
159 void
160 mpdclient_disconnect(struct mpdclient *c)
162         if (c->connection)
163                 mpd_connection_free(c->connection);
164         c->connection = NULL;
166         if (c->status)
167                 mpd_status_free(c->status);
168         c->status = NULL;
170         playlist_clear(&c->playlist);
172         if (c->song)
173                 c->song = NULL;
176 bool
177 mpdclient_connect(struct mpdclient *c,
178                   const gchar *host,
179                   gint port,
180                   gfloat _timeout,
181                   const gchar *password)
183         /* close any open connection */
184         if( c->connection )
185                 mpdclient_disconnect(c);
187         /* connect to MPD */
188         c->connection = mpd_connection_new(host, port, _timeout * 1000);
189         if (c->connection == NULL)
190                 g_error("Out of memory");
192         if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
193                 mpdclient_handle_error(c);
194                 mpdclient_disconnect(c);
195                 return false;
196         }
198         /* send password */
199         if (password != NULL && !mpd_run_password(c->connection, password)) {
200                 mpdclient_handle_error(c);
201                 mpdclient_disconnect(c);
202                 return false;
203         }
205         return true;
208 bool
209 mpdclient_update(struct mpdclient *c)
211         bool retval;
213         c->volume = -1;
215         if (MPD_ERROR(c))
216                 return false;
218         /* always announce these options as long as we don't have real
219            "idle" support */
220         c->events |= MPD_IDLE_PLAYER|MPD_IDLE_OPTIONS;
222         /* free the old status */
223         if (c->status)
224                 mpd_status_free(c->status);
226         /* retrieve new status */
227         c->status = mpd_run_status(c->connection);
228         if (c->status == NULL)
229                 return mpdclient_handle_error(c) == 0;
231         if (c->update_id != mpd_status_get_update_id(c->status)) {
232                 c->events |= MPD_IDLE_UPDATE;
234                 if (c->update_id > 0)
235                         c->events |= MPD_IDLE_DATABASE;
236         }
238         c->update_id = mpd_status_get_update_id(c->status);
240         if (c->volume != mpd_status_get_volume(c->status))
241                 c->events |= MPD_IDLE_MIXER;
243         c->volume = mpd_status_get_volume(c->status);
245         /* check if the playlist needs an update */
246         if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
247                 c->events |= MPD_IDLE_PLAYLIST;
249                 if (!playlist_is_empty(&c->playlist))
250                         retval = mpdclient_playlist_update_changes(c);
251                 else
252                         retval = mpdclient_playlist_update(c);
253         } else
254                 retval = true;
256         /* update the current song */
257         if (!c->song || mpd_status_get_song_id(c->status)) {
258                 c->song = playlist_get_song(&c->playlist,
259                                             mpd_status_get_song_pos(c->status));
260         }
262         return retval;
266 /****************************************************************************/
267 /*** MPD Commands  **********************************************************/
268 /****************************************************************************/
270 gint
271 mpdclient_cmd_play(struct mpdclient *c, gint idx)
273         const struct mpd_song *song = playlist_get_song(&c->playlist, idx);
275         if (MPD_ERROR(c))
276                 return -1;
278         if (song)
279                 mpd_send_play_id(c->connection, mpd_song_get_id(song));
280         else
281                 mpd_send_play(c->connection);
283         return mpdclient_finish_command(c);
286 gint
287 mpdclient_cmd_crop(struct mpdclient *c)
289         struct mpd_status *status;
290         bool playing;
291         int length, current;
293         if (MPD_ERROR(c))
294                 return -1;
296         status = mpd_run_status(c->connection);
297         if (status == NULL)
298                 return mpdclient_handle_error(c);
300         playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
301                 mpd_status_get_state(status) == MPD_STATE_PAUSE;
302         length = mpd_status_get_queue_length(status);
303         current = mpd_status_get_song_pos(status);
305         mpd_status_free(status);
307         if (!playing || length < 2)
308                 return 0;
310         mpd_command_list_begin(c->connection, false);
312         while (--length >= 0)
313                 if (length != current)
314                         mpd_send_delete(c->connection, length);
316         mpd_command_list_end(c->connection);
318         return mpdclient_finish_command(c);
321 gint
322 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
324         mpd_send_shuffle_range(c->connection, start, end);
325         return mpdclient_finish_command(c);
328 gint
329 mpdclient_cmd_clear(struct mpdclient *c)
331         gint retval = 0;
333         if (MPD_ERROR(c))
334                 return -1;
336         mpd_send_clear(c->connection);
337         retval = mpdclient_finish_command(c);
339         if (retval)
340                 c->events |= MPD_IDLE_PLAYLIST;
342         return retval;
345 gint
346 mpdclient_cmd_volume(struct mpdclient *c, gint value)
348         if (MPD_ERROR(c))
349                 return -1;
351         mpd_send_set_volume(c->connection, value);
352         return mpdclient_finish_command(c);
355 gint mpdclient_cmd_volume_up(struct mpdclient *c)
357         if (MPD_ERROR(c))
358                 return -1;
360         if (c->status == NULL ||
361             mpd_status_get_volume(c->status) == -1)
362                 return 0;
364         if (c->volume < 0)
365                 c->volume = mpd_status_get_volume(c->status);
367         if (c->volume >= 100)
368                 return 0;
370         return mpdclient_cmd_volume(c, ++c->volume);
373 gint mpdclient_cmd_volume_down(struct mpdclient *c)
375         if (MPD_ERROR(c))
376                 return -1;
378         if (c->status == NULL || mpd_status_get_volume(c->status) < 0)
379                 return 0;
381         if (c->volume < 0)
382                 c->volume = mpd_status_get_volume(c->status);
384         if (c->volume <= 0)
385                 return 0;
387         return mpdclient_cmd_volume(c, --c->volume);
390 gint
391 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
393         if (MPD_ERROR(c))
394                 return -1;
396         mpd_send_add(c->connection, path_utf8);
397         return mpdclient_finish_command(c);
400 gint
401 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
403         gint retval = 0;
405         assert(c != NULL);
406         assert(song != NULL);
408         if (MPD_ERROR(c))
409                 return -1;
411         /* send the add command to mpd */
412         mpd_send_add(c->connection, mpd_song_get_uri(song));
413         if( (retval=mpdclient_finish_command(c)) )
414                 return retval;
416 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
417         /* add the song to playlist */
418         playlist_append(&c->playlist, song);
420         /* increment the playlist id, so we don't retrieve a new playlist */
421         c->playlist.id++;
423         c->events |= MPD_IDLE_PLAYLIST;
424 #endif
426         return 0;
429 gint
430 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
432         gint retval = 0;
433         struct mpd_song *song;
435         if (MPD_ERROR(c))
436                 return -1;
438         if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
439                 return -1;
441         song = playlist_get(&c->playlist, idx);
443         /* send the delete command to mpd */
444         mpd_send_delete_id(c->connection, mpd_song_get_id(song));
445         if( (retval=mpdclient_finish_command(c)) )
446                 return retval;
448 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
449         /* increment the playlist id, so we don't retrieve a new playlist */
450         c->playlist.id++;
452         /* remove the song from the playlist */
453         playlist_remove_reuse(&c->playlist, idx);
455         c->events |= MPD_IDLE_PLAYLIST;
457         /* remove references to the song */
458         if (c->song == song)
459                 c->song = NULL;
461         mpd_song_free(song);
462 #endif
464         return 0;
467 gint
468 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
470         gint n;
471         struct mpd_song *song1, *song2;
473         if (MPD_ERROR(c))
474                 return -1;
476         if (old_index == new_index || new_index < 0 ||
477             (guint)new_index >= c->playlist.list->len)
478                 return -1;
480         song1 = playlist_get(&c->playlist, old_index);
481         song2 = playlist_get(&c->playlist, new_index);
483         /* send the move command to mpd */
484         mpd_send_swap_id(c->connection,
485                          mpd_song_get_id(song1), mpd_song_get_id(song2));
486         if( (n=mpdclient_finish_command(c)) )
487                 return n;
489 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
490         /* update the playlist */
491         playlist_swap(&c->playlist, old_index, new_index);
493         /* increment the playlist id, so we don't retrieve a new playlist */
494         c->playlist.id++;
495 #endif
497         c->events |= MPD_IDLE_PLAYLIST;
499         return 0;
502 gint
503 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
505         gint retval = 0;
507         if (MPD_ERROR(c))
508                 return -1;
510         mpd_send_save(c->connection, filename_utf8);
511         if ((retval = mpdclient_finish_command(c)) == 0) {
512                 c->events |= MPD_IDLE_STORED_PLAYLIST;
513         }
515         return retval;
518 gint
519 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
521         if (MPD_ERROR(c))
522                 return -1;
524         mpd_send_load(c->connection, filename_utf8);
525         return mpdclient_finish_command(c);
528 gint
529 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
531         gint retval = 0;
533         if (MPD_ERROR(c))
534                 return -1;
536         mpd_send_rm(c->connection, filename_utf8);
537         if ((retval = mpdclient_finish_command(c)) == 0)
538                 c->events |= MPD_IDLE_STORED_PLAYLIST;
540         return retval;
544 /****************************************************************************/
545 /*** Playlist management functions ******************************************/
546 /****************************************************************************/
548 /* update playlist */
549 bool
550 mpdclient_playlist_update(struct mpdclient *c)
552         struct mpd_entity *entity;
554         if (MPD_ERROR(c))
555                 return false;
557         playlist_clear(&c->playlist);
559         mpd_send_list_queue_meta(c->connection);
560         while ((entity = mpd_recv_entity(c->connection))) {
561                 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
562                         playlist_append(&c->playlist, mpd_entity_get_song(entity));
564                 mpd_entity_free(entity);
565         }
567         c->playlist.id = mpd_status_get_queue_version(c->status);
568         c->song = NULL;
570         return mpdclient_finish_command(c) == 0;
573 /* update playlist (plchanges) */
574 bool
575 mpdclient_playlist_update_changes(struct mpdclient *c)
577         struct mpd_song *song;
578         guint length;
580         if (MPD_ERROR(c))
581                 return false;
583         mpd_send_queue_changes_meta(c->connection, c->playlist.id);
585         while ((song = mpd_recv_song(c->connection)) != NULL) {
586                 int pos = mpd_song_get_pos(song);
588                 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
589                         /* update song */
590                         playlist_replace(&c->playlist, pos, song);
591                 } else {
592                         /* add a new song */
593                         playlist_append(&c->playlist, song);
594                 }
596                 mpd_song_free(song);
597         }
599         /* remove trailing songs */
601         length = mpd_status_get_queue_length(c->status);
602         while (length < c->playlist.list->len) {
603                 guint pos = c->playlist.list->len - 1;
605                 /* Remove the last playlist entry */
606                 playlist_remove(&c->playlist, pos);
607         }
609         c->song = NULL;
610         c->playlist.id = mpd_status_get_queue_version(c->status);
612         return mpdclient_finish_command(c) == 0;
616 /****************************************************************************/
617 /*** Filelist functions *****************************************************/
618 /****************************************************************************/
620 struct filelist *
621 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
623         struct filelist *filelist;
624         struct mpd_entity *entity;
626         if (MPD_ERROR(c))
627                 return NULL;
629         mpd_send_list_meta(c->connection, path);
630         filelist = filelist_new();
632         while ((entity = mpd_recv_entity(c->connection)) != NULL)
633                 filelist_append(filelist, entity);
635         if (mpdclient_finish_command(c)) {
636                 filelist_free(filelist);
637                 return NULL;
638         }
640         filelist_sort_dir_play(filelist, compare_filelistentry);
642         return filelist;
645 static struct filelist *
646 mpdclient_recv_filelist_response(struct mpdclient *c)
648         struct filelist *filelist;
649         struct mpd_entity *entity;
651         filelist = filelist_new();
653         while ((entity = mpd_recv_entity(c->connection)) != NULL)
654                 filelist_append(filelist, entity);
656         if (mpdclient_finish_command(c)) {
657                 filelist_free(filelist);
658                 return NULL;
659         }
661         return filelist;
664 struct filelist *
665 mpdclient_filelist_search(struct mpdclient *c,
666                           int exact_match,
667                           enum mpd_tag_type tag,
668                           gchar *filter_utf8)
670         if (MPD_ERROR(c))
671                 return NULL;
673         mpd_search_db_songs(c->connection, exact_match);
674         mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
675                                       tag, filter_utf8);
676         mpd_search_commit(c->connection);
678         return mpdclient_recv_filelist_response(c);
681 int
682 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
684         guint i;
686         if (MPD_ERROR(c))
687                 return -1;
689         if (filelist_is_empty(fl))
690                 return 0;
692         mpd_command_list_begin(c->connection, false);
694         for (i = 0; i < filelist_length(fl); ++i) {
695                 struct filelist_entry *entry = filelist_get(fl, i);
696                 struct mpd_entity *entity  = entry->entity;
698                 if (entity != NULL &&
699                     mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
700                         const struct mpd_song *song =
701                                 mpd_entity_get_song(entity);
702                         const char *uri = mpd_song_get_uri(song);
704                         if (uri != NULL)
705                                 mpd_send_add(c->connection, uri);
706                 }
707         }
709         mpd_command_list_end(c->connection);
710         return mpdclient_finish_command(c);
713 GList *
714 mpdclient_get_artists(struct mpdclient *c)
716         GList *list = NULL;
717         struct mpd_pair *pair;
719         if (MPD_ERROR(c))
720                return NULL;
722         mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
723         mpd_search_commit(c->connection);
725         while ((pair = mpd_recv_pair_tag(c->connection,
726                                          MPD_TAG_ARTIST)) != NULL) {
727                 list = g_list_append(list, g_strdup(pair->value));
728                 mpd_return_pair(c->connection, pair);
729         }
731         if (mpdclient_finish_command(c))
732                 return string_list_free(list);
734         return list;
737 GList *
738 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
740         GList *list = NULL;
741         struct mpd_pair *pair;
743         if (MPD_ERROR(c))
744                return NULL;
746         mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
747         if (artist_utf8 != NULL)
748                 mpd_search_add_tag_constraint(c->connection,
749                                               MPD_OPERATOR_DEFAULT,
750                                               MPD_TAG_ARTIST, artist_utf8);
751         mpd_search_commit(c->connection);
753         while ((pair = mpd_recv_pair_tag(c->connection,
754                                          MPD_TAG_ALBUM)) != NULL) {
755                 list = g_list_append(list, g_strdup(pair->value));
756                 mpd_return_pair(c->connection, pair);
757         }
759         if (mpdclient_finish_command(c))
760                 return string_list_free(list);
762         return list;