Code

use libmpdclient2
[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 "screen_utils.h"
22 #include "config.h"
23 #include "options.h"
24 #include "strfsong.h"
25 #include "utils.h"
27 #include <mpd/client.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <string.h>
34 #undef  ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
35 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
36 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
37 #define ENABLE_SONG_ID
38 #define ENABLE_PLCHANGES
40 #define BUFSIZE 1024
42 static bool
43 MPD_ERROR(const struct mpdclient *client)
44 {
45         return client->connection == NULL ||
46                 mpd_connection_get_error(client->connection) != MPD_ERROR_SUCCESS;
47 }
49 /* filelist sorting functions */
50 static gint
51 compare_filelistentry(gconstpointer filelist_entry1,
52                           gconstpointer filelist_entry2)
53 {
54         const struct mpd_entity *e1, *e2;
55         int n = 0;
57         e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
58         e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
60         if (e1 != NULL && e2 != NULL &&
61             mpd_entity_get_type(e1) == mpd_entity_get_type(e2)) {
62                 switch (mpd_entity_get_type(e1)) {
63                 case MPD_ENTITY_TYPE_UNKNOWN:
64                         break;
65                 case MPD_ENTITY_TYPE_DIRECTORY:
66                         n = g_utf8_collate(mpd_directory_get_path(mpd_entity_get_directory(e1)),
67                                            mpd_directory_get_path(mpd_entity_get_directory(e2)));
68                         break;
69                 case MPD_ENTITY_TYPE_SONG:
70                         break;
71                 case MPD_ENTITY_TYPE_PLAYLIST:
72                         n = g_utf8_collate(mpd_playlist_get_path(mpd_entity_get_playlist(e1)),
73                                            mpd_playlist_get_path(mpd_entity_get_playlist(e2)));
74                 }
75         }
76         return n;
77 }
79 /* sort by list-format */
80 gint
81 compare_filelistentry_format(gconstpointer filelist_entry1,
82                              gconstpointer filelist_entry2)
83 {
84         const struct mpd_entity *e1, *e2;
85         char key1[BUFSIZE], key2[BUFSIZE];
86         int n = 0;
88         e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
89         e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
91         if (e1 && e2 &&
92             mpd_entity_get_type(e1) == MPD_ENTITY_TYPE_SONG &&
93             mpd_entity_get_type(e2) == MPD_ENTITY_TYPE_SONG) {
94                 strfsong(key1, BUFSIZE, options.list_format, mpd_entity_get_song(e1));
95                 strfsong(key2, BUFSIZE, options.list_format, mpd_entity_get_song(e2));
96                 n = strcmp(key1,key2);
97         }
99         return n;
103 /* Error callbacks */
104 static gint
105 error_cb(mpdclient_t *c, gint error, const gchar *msg)
107         GList *list = c->error_callbacks;
109         if (list == NULL)
110                 fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
112         while (list) {
113                 mpdc_error_cb_t cb = list->data;
114                 if (cb)
115                         cb(c, error, msg);
116                 list = list->next;
117         }
119         mpd_connection_clear_error(c->connection);
120         return error;
124 /****************************************************************************/
125 /*** mpdclient functions ****************************************************/
126 /****************************************************************************/
128 static gint
129 mpdclient_handle_error(mpdclient_t *c)
131         enum mpd_error error = mpd_connection_get_error(c->connection);
132         bool is_fatal = error != MPD_ERROR_SERVER;
134         assert(error != MPD_ERROR_SUCCESS);
136         if (error == MPD_ERROR_SERVER &&
137             mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION &&
138             screen_auth(c) == 0)
139                 return 0;
141         if (error == MPD_ERROR_SERVER)
142                 error = error | (mpd_connection_get_server_error(c->connection) << 8);
144         error_cb(c, error, mpd_connection_get_error_message(c->connection));
146         if (is_fatal)
147                 mpdclient_disconnect(c);
149         return error;
152 gint
153 mpdclient_finish_command(mpdclient_t *c)
155         return mpd_response_finish(c->connection)
156                 ? 0 : mpdclient_handle_error(c);
159 mpdclient_t *
160 mpdclient_new(void)
162         mpdclient_t *c;
164         c = g_malloc0(sizeof(mpdclient_t));
165         playlist_init(&c->playlist);
166         c->volume = MPD_STATUS_NO_VOLUME;
168         return c;
171 void
172 mpdclient_free(mpdclient_t *c)
174         mpdclient_disconnect(c);
176         mpdclient_playlist_free(&c->playlist);
178         g_list_free(c->error_callbacks);
179         g_list_free(c->playlist_callbacks);
180         g_list_free(c->browse_callbacks);
181         g_free(c);
184 gint
185 mpdclient_disconnect(mpdclient_t *c)
187         if (c->connection)
188                 mpd_connection_free(c->connection);
189         c->connection = NULL;
191         if (c->status)
192                 mpd_status_free(c->status);
193         c->status = NULL;
195         playlist_clear(&c->playlist);
197         if (c->song)
198                 c->song = NULL;
200         return 0;
203 gint
204 mpdclient_connect(mpdclient_t *c,
205                   const gchar *host,
206                   gint port,
207                   gfloat _timeout,
208                   const gchar *password)
210         gint retval = 0;
212         /* close any open connection */
213         if( c->connection )
214                 mpdclient_disconnect(c);
216         /* connect to MPD */
217         c->connection = mpd_connection_new(host, port, _timeout * 1000);
218         if (c->connection == NULL)
219                 return error_cb(c, MPD_ERROR_OOM, "Out of memory");
221         if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
222                 retval = error_cb(c, mpd_connection_get_error(c->connection),
223                                   mpd_connection_get_error_message(c->connection));
224                 if (retval != 0) {
225                         mpd_connection_free(c->connection);
226                         c->connection = NULL;
227                 }
229                 return retval;
230         }
232         /* send password */
233         if( password ) {
234                 mpd_send_password(c->connection, password);
235                 retval = mpdclient_finish_command(c);
236         }
237         c->need_update = TRUE;
239         return retval;
242 gint
243 mpdclient_update(mpdclient_t *c)
245         gint retval = 0;
247         c->volume = MPD_STATUS_NO_VOLUME;
249         if (MPD_ERROR(c))
250                 return -1;
252         /* free the old status */
253         if (c->status)
254                 mpd_status_free(c->status);
256         /* retrieve new status */
257         c->status = mpd_run_status(c->connection);
258         if (c->status == NULL)
259                 return mpdclient_handle_error(c);
261         if (c->updatingdb &&
262             c->updatingdb != mpd_status_get_update_id(c->status))
263                 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
265         c->updatingdb = mpd_status_get_update_id(c->status);
266         c->volume = mpd_status_get_volume(c->status);
268         /* check if the playlist needs an update */
269         if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
270                 if (playlist_is_empty(&c->playlist))
271                         retval = mpdclient_playlist_update_changes(c);
272                 else
273                         retval = mpdclient_playlist_update(c);
274         }
276         /* update the current song */
277         if (!c->song || mpd_status_get_song_id(c->status)) {
278                 c->song = playlist_get_song(c, mpd_status_get_song_pos(c->status));
279         }
281         c->need_update = FALSE;
283         return retval;
287 /****************************************************************************/
288 /*** MPD Commands  **********************************************************/
289 /****************************************************************************/
291 gint
292 mpdclient_cmd_play(mpdclient_t *c, gint idx)
294 #ifdef ENABLE_SONG_ID
295         struct mpd_song *song = playlist_get_song(c, idx);
297         if (MPD_ERROR(c))
298                 return -1;
300         if (song)
301                 mpd_send_play_id(c->connection, mpd_song_get_id(song));
302         else
303                 mpd_send_play(c->connection);
304 #else
305         if (MPD_ERROR(c))
306                 return -1;
308         mpd_sendPlayCommand(c->connection, idx);
309 #endif
310         c->need_update = TRUE;
311         return mpdclient_finish_command(c);
314 gint
315 mpdclient_cmd_pause(mpdclient_t *c, gint value)
317         if (MPD_ERROR(c))
318                 return -1;
320         mpd_send_pause(c->connection, value);
321         return mpdclient_finish_command(c);
324 gint
325 mpdclient_cmd_crop(mpdclient_t *c)
327         struct mpd_status *status;
328         bool playing;
329         int length, current;
331         if (MPD_ERROR(c))
332                 return -1;
334         status = mpd_run_status(c->connection);
335         if (status == NULL)
336                 return mpdclient_handle_error(c);
338         playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
339                 mpd_status_get_state(status) == MPD_STATE_PAUSE;
340         length = mpd_status_get_queue_length(status);
341         current = mpd_status_get_song_pos(status);
343         mpd_status_free(status);
345         if (!playing || length < 2)
346                 return 0;
348         mpd_command_list_begin(c->connection, false);
350         while (--length >= 0)
351                 if (length != current)
352                         mpd_send_delete(c->connection, length);
354         mpd_command_list_end(c->connection);
356         return mpdclient_finish_command(c);
359 gint
360 mpdclient_cmd_stop(mpdclient_t *c)
362         if (MPD_ERROR(c))
363                 return -1;
365         mpd_send_stop(c->connection);
366         return mpdclient_finish_command(c);
369 gint
370 mpdclient_cmd_next(mpdclient_t *c)
372         if (MPD_ERROR(c))
373                 return -1;
375         mpd_send_next(c->connection);
376         c->need_update = TRUE;
377         return mpdclient_finish_command(c);
380 gint
381 mpdclient_cmd_prev(mpdclient_t *c)
383         if (MPD_ERROR(c))
384                 return -1;
386         mpd_send_previous(c->connection);
387         c->need_update = TRUE;
388         return mpdclient_finish_command(c);
391 gint
392 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
394         if (MPD_ERROR(c))
395                 return -1;
397         mpd_send_seek_id(c->connection, id, pos);
398         return mpdclient_finish_command(c);
401 gint
402 mpdclient_cmd_shuffle(mpdclient_t *c)
404         if (MPD_ERROR(c))
405                 return -1;
407         mpd_send_shuffle(c->connection);
408         c->need_update = TRUE;
409         return mpdclient_finish_command(c);
412 gint
413 mpdclient_cmd_shuffle_range(mpdclient_t *c, guint start, guint end)
415         mpd_send_shuffle_range(c->connection, start, end);
416         c->need_update = TRUE;
417         return mpdclient_finish_command(c);
420 gint
421 mpdclient_cmd_clear(mpdclient_t *c)
423         gint retval = 0;
425         if (MPD_ERROR(c))
426                 return -1;
428         mpd_send_clear(c->connection);
429         retval = mpdclient_finish_command(c);
430         /* call playlist updated callback */
431         mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
432         c->need_update = TRUE;
433         return retval;
436 gint
437 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
439         if (MPD_ERROR(c))
440                 return -1;
442         mpd_send_repeat(c->connection, value);
443         return mpdclient_finish_command(c);
446 gint
447 mpdclient_cmd_random(mpdclient_t *c, gint value)
449         if (MPD_ERROR(c))
450                 return -1;
452         mpd_send_random(c->connection, value);
453         return mpdclient_finish_command(c);
456 gint
457 mpdclient_cmd_single(mpdclient_t *c, gint value)
459         if (MPD_ERROR(c))
460                 return -1;
462         mpd_send_single(c->connection, value);
463         return mpdclient_finish_command(c);
466 gint
467 mpdclient_cmd_consume(mpdclient_t *c, gint value)
469         if (MPD_ERROR(c))
470                 return -1;
472         mpd_send_consume(c->connection, value);
473         return mpdclient_finish_command(c);
476 gint
477 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
479         if (MPD_ERROR(c))
480                 return -1;
482         mpd_send_crossfade(c->connection, value);
483         return mpdclient_finish_command(c);
486 gint
487 mpdclient_cmd_db_update(mpdclient_t *c, const gchar *path)
489         gint ret;
491         if (MPD_ERROR(c))
492                 return -1;
494         mpd_send_update(c->connection, path ? path : "");
495         ret = mpdclient_finish_command(c);
497         if (ret == 0)
498                 /* set updatingDb to make sure the browse callback
499                    gets called even if the update has finished before
500                    status is updated */
501                 c->updatingdb = 1;
503         return ret;
506 gint
507 mpdclient_cmd_volume(mpdclient_t *c, gint value)
509         if (MPD_ERROR(c))
510                 return -1;
512         mpd_send_set_volume(c->connection, value);
513         return mpdclient_finish_command(c);
516 gint mpdclient_cmd_volume_up(struct mpdclient *c)
518         if (MPD_ERROR(c))
519                 return -1;
521         if (c->status == NULL ||
522             mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
523                 return 0;
525         if (c->volume == MPD_STATUS_NO_VOLUME)
526                 c->volume = mpd_status_get_volume(c->status);
528         if (c->volume >= 100)
529                 return 0;
531         return mpdclient_cmd_volume(c, ++c->volume);
534 gint mpdclient_cmd_volume_down(struct mpdclient *c)
536         if (MPD_ERROR(c))
537                 return -1;
539         if (c->status == NULL ||
540             mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
541                 return 0;
543         if (c->volume == MPD_STATUS_NO_VOLUME)
544                 c->volume = mpd_status_get_volume(c->status);
546         if (c->volume <= 0)
547                 return 0;
549         return mpdclient_cmd_volume(c, --c->volume);
552 gint
553 mpdclient_cmd_add_path(mpdclient_t *c, const gchar *path_utf8)
555         if (MPD_ERROR(c))
556                 return -1;
558         mpd_send_add(c->connection, path_utf8);
559         return mpdclient_finish_command(c);
562 gint
563 mpdclient_cmd_add(mpdclient_t *c, const struct mpd_song *song)
565         gint retval = 0;
567         if (MPD_ERROR(c))
568                 return -1;
570         if (song == NULL)
571                 return -1;
573         /* send the add command to mpd */
574         mpd_send_add(c->connection, mpd_song_get_uri(song));
575         if( (retval=mpdclient_finish_command(c)) )
576                 return retval;
578 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
579         /* add the song to playlist */
580         playlist_append(&c->playlist, song);
582         /* increment the playlist id, so we don't retrieve a new playlist */
583         c->playlist.id++;
585         /* call playlist updated callback */
586         mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
587 #else
588         c->need_update = TRUE;
589 #endif
591         return 0;
594 gint
595 mpdclient_cmd_delete(mpdclient_t *c, gint idx)
597         gint retval = 0;
598         struct mpd_song *song;
600         if (MPD_ERROR(c))
601                 return -1;
603         if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
604                 return -1;
606         song = playlist_get(&c->playlist, idx);
608         /* send the delete command to mpd */
609 #ifdef ENABLE_SONG_ID
610         mpd_send_delete_id(c->connection, mpd_song_get_id(song));
611 #else
612         mpd_send_delete(c->connection, idx);
613 #endif
614         if( (retval=mpdclient_finish_command(c)) )
615                 return retval;
617 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
618         /* increment the playlist id, so we don't retrieve a new playlist */
619         c->playlist.id++;
621         /* remove the song from the playlist */
622         playlist_remove_reuse(&c->playlist, idx);
624         /* call playlist updated callback */
625         mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
627         /* remove references to the song */
628         if (c->song == song) {
629                 c->song = NULL;
630                 c->need_update = TRUE;
631         }
633         mpd_song_free(song);
635 #else
636         c->need_update = TRUE;
637 #endif
639         return 0;
642 gint
643 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
645         gint n;
646         struct mpd_song *song1, *song2;
648         if (MPD_ERROR(c))
649                 return -1;
651         if (old_index == new_index || new_index < 0 ||
652             (guint)new_index >= c->playlist.list->len)
653                 return -1;
655         song1 = playlist_get(&c->playlist, old_index);
656         song2 = playlist_get(&c->playlist, new_index);
658         /* send the move command to mpd */
659 #ifdef ENABLE_SONG_ID
660         mpd_send_swap_id(c->connection,
661                          mpd_song_get_id(song1), mpd_song_get_id(song2));
662 #else
663         mpd_send_move(c->connection, old_index, new_index);
664 #endif
665         if( (n=mpdclient_finish_command(c)) )
666                 return n;
668 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
669         /* update the playlist */
670         playlist_swap(&c->playlist, old_index, new_index);
672         /* increment the playlist id, so we don't retrieve a new playlist */
673         c->playlist.id++;
675 #else
676         c->need_update = TRUE;
677 #endif
679         /* call playlist updated callback */
680         mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
682         return 0;
685 gint
686 mpdclient_cmd_save_playlist(mpdclient_t *c, const gchar *filename_utf8)
688         gint retval = 0;
690         if (MPD_ERROR(c))
691                 return -1;
693         mpd_send_save(c->connection, filename_utf8);
694         if ((retval = mpdclient_finish_command(c)) == 0)
695                 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
696         return retval;
699 gint
700 mpdclient_cmd_load_playlist(mpdclient_t *c, const gchar *filename_utf8)
702         if (MPD_ERROR(c))
703                 return -1;
705         mpd_send_load(c->connection, filename_utf8);
706         c->need_update = TRUE;
707         return mpdclient_finish_command(c);
710 gint
711 mpdclient_cmd_delete_playlist(mpdclient_t *c, const gchar *filename_utf8)
713         gint retval = 0;
715         if (MPD_ERROR(c))
716                 return -1;
718         mpd_send_rm(c->connection, filename_utf8);
719         if ((retval = mpdclient_finish_command(c)) == 0)
720                 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
721         return retval;
725 /****************************************************************************/
726 /*** Callback management functions ******************************************/
727 /****************************************************************************/
729 static void
730 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
732         while (list) {
733                 mpdc_list_cb_t fn = list->data;
735                 fn(c, event, data);
736                 list = list->next;
737         }
740 void
741 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
743         do_list_callbacks(c, c->playlist_callbacks, event, data);
746 void
747 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
749         c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
752 void
753 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
755         c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
758 void
759 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
761         do_list_callbacks(c, c->browse_callbacks, event, data);
765 void
766 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
768         c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
771 void
772 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
774         c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
777 void
778 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
780         c->error_callbacks = g_list_append(c->error_callbacks, cb);
783 void
784 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
786         c->error_callbacks = g_list_remove(c->error_callbacks, cb);
790 /****************************************************************************/
791 /*** Playlist management functions ******************************************/
792 /****************************************************************************/
794 /* update playlist */
795 gint
796 mpdclient_playlist_update(mpdclient_t *c)
798         struct mpd_entity *entity;
800         if (MPD_ERROR(c))
801                 return -1;
803         playlist_clear(&c->playlist);
805         mpd_send_list_queue_meta(c->connection);
806         while ((entity = mpd_recv_entity(c->connection))) {
807                 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
808                         playlist_append(&c->playlist, mpd_entity_get_song(entity));
810                 mpd_entity_free(entity);
811         }
813         c->playlist.id = mpd_status_get_queue_version(c->status);
814         c->song = NULL;
816         /* call playlist updated callbacks */
817         mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
819         return mpdclient_finish_command(c);
822 #ifdef ENABLE_PLCHANGES
824 /* update playlist (plchanges) */
825 gint
826 mpdclient_playlist_update_changes(mpdclient_t *c)
828         struct mpd_song *song;
829         guint length;
831         if (MPD_ERROR(c))
832                 return -1;
834         mpd_send_queue_changes_meta(c->connection, c->playlist.id);
836         while ((song = mpd_recv_song(c->connection)) != NULL) {
837                 int pos = mpd_song_get_pos(song);
839                 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
840                         /* update song */
841                         playlist_replace(&c->playlist, pos, song);
842                 } else {
843                         /* add a new song */
844                         playlist_append(&c->playlist, song);
845                 }
847                 mpd_song_free(song);
848         }
850         /* remove trailing songs */
852         length = mpd_status_get_queue_length(c->status);
853         while (length < c->playlist.list->len) {
854                 guint pos = c->playlist.list->len - 1;
856                 /* Remove the last playlist entry */
857                 playlist_remove(&c->playlist, pos);
858         }
860         c->song = NULL;
861         c->playlist.id = mpd_status_get_queue_version(c->status);
863         mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
865         return 0;
868 #else
869 gint
870 mpdclient_playlist_update_changes(mpdclient_t *c)
872         return mpdclient_playlist_update(c);
874 #endif
877 /****************************************************************************/
878 /*** Filelist functions *****************************************************/
879 /****************************************************************************/
881 mpdclient_filelist_t *
882 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
884         mpdclient_filelist_t *filelist;
885         struct mpd_entity *entity;
887         if (MPD_ERROR(c))
888                 return NULL;
890         mpd_send_list_meta(c->connection, path);
891         filelist = filelist_new();
892         if (path && path[0] && strcmp(path, "/"))
893                 /* add a dummy entry for ./.. */
894                 filelist_append(filelist, NULL);
896         while ((entity = mpd_recv_entity(c->connection)) != NULL)
897                 filelist_append(filelist, entity);
899         /* If there's an error, ignore it.  We'll return an empty filelist. */
900         mpdclient_finish_command(c);
902         filelist_sort_dir_play(filelist, compare_filelistentry);
904         return filelist;
907 static struct filelist *
908 mpdclient_recv_filelist_response(struct mpdclient *c)
910         struct filelist *filelist;
911         struct mpd_entity *entity;
913         filelist = filelist_new();
915         while ((entity = mpd_recv_entity(c->connection)) != NULL)
916                 filelist_append(filelist, entity);
918         if (mpdclient_finish_command(c)) {
919                 filelist_free(filelist);
920                 return NULL;
921         }
923         return filelist;
926 mpdclient_filelist_t *
927 mpdclient_filelist_search(mpdclient_t *c,
928                           int exact_match,
929                           enum mpd_tag_type tag,
930                           gchar *filter_utf8)
932         if (MPD_ERROR(c))
933                 return NULL;
935         mpd_search_db_songs(c->connection, exact_match);
936         mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
937                                       tag, filter_utf8);
938         mpd_search_commit(c->connection);
940         return mpdclient_recv_filelist_response(c);
943 int
944 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
946         guint i;
948         if (MPD_ERROR(c))
949                 return -1;
951         if (filelist_is_empty(fl))
952                 return 0;
954         mpd_command_list_begin(c->connection, false);
956         for (i = 0; i < filelist_length(fl); ++i) {
957                 filelist_entry_t *entry = filelist_get(fl, i);
958                 struct mpd_entity *entity  = entry->entity;
960                 if (entity != NULL &&
961                     mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
962                         const struct mpd_song *song =
963                                 mpd_entity_get_song(entity);
964                         const char *uri = mpd_song_get_uri(song);
966                         if (uri != NULL)
967                                 mpd_send_add(c->connection, uri);
968                 }
969         }
971         mpd_command_list_end(c->connection);
972         return mpdclient_finish_command(c);
975 GList *
976 mpdclient_get_artists(mpdclient_t *c)
978         GList *list = NULL;
979         struct mpd_pair *pair;
981         if (MPD_ERROR(c))
982                return NULL;
984         mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
985         mpd_search_commit(c->connection);
987         while ((pair = mpd_recv_pair_tag(c->connection,
988                                          MPD_TAG_ARTIST)) != NULL) {
989                 list = g_list_append(list, g_strdup(pair->value));
990                 mpd_return_pair(c->connection, pair);
991         }
993         if (mpdclient_finish_command(c))
994                 return string_list_free(list);
996         return list;
999 GList *
1000 mpdclient_get_albums(mpdclient_t *c, const gchar *artist_utf8)
1002         GList *list = NULL;
1003         struct mpd_pair *pair;
1005         if (MPD_ERROR(c))
1006                return NULL;
1008         mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
1009         if (artist_utf8 != NULL)
1010                 mpd_search_add_tag_constraint(c->connection,
1011                                               MPD_OPERATOR_DEFAULT,
1012                                               MPD_TAG_ARTIST, artist_utf8);
1013         mpd_search_commit(c->connection);
1015         while ((pair = mpd_recv_pair_tag(c->connection,
1016                                          MPD_TAG_ALBUM)) != NULL) {
1017                 list = g_list_append(list, g_strdup(pair->value));
1018                 mpd_return_pair(c->connection, pair);
1019         }
1021         if (mpdclient_finish_command(c))
1022                 return string_list_free(list);
1024         return list;