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;
100 }
103 /* Error callbacks */
104 static gint
105 error_cb(mpdclient_t *c, gint error, const gchar *msg)
106 {
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;
121 }
124 /****************************************************************************/
125 /*** mpdclient functions ****************************************************/
126 /****************************************************************************/
128 static gint
129 mpdclient_handle_error(mpdclient_t *c)
130 {
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;
150 }
152 gint
153 mpdclient_finish_command(mpdclient_t *c)
154 {
155 return mpd_response_finish(c->connection)
156 ? 0 : mpdclient_handle_error(c);
157 }
159 mpdclient_t *
160 mpdclient_new(void)
161 {
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;
169 }
171 void
172 mpdclient_free(mpdclient_t *c)
173 {
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);
182 }
184 gint
185 mpdclient_disconnect(mpdclient_t *c)
186 {
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;
201 }
203 gint
204 mpdclient_connect(mpdclient_t *c,
205 const gchar *host,
206 gint port,
207 gfloat _timeout,
208 const gchar *password)
209 {
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;
240 }
242 gint
243 mpdclient_update(mpdclient_t *c)
244 {
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;
284 }
287 /****************************************************************************/
288 /*** MPD Commands **********************************************************/
289 /****************************************************************************/
291 gint
292 mpdclient_cmd_play(mpdclient_t *c, gint idx)
293 {
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);
312 }
314 gint
315 mpdclient_cmd_pause(mpdclient_t *c, gint value)
316 {
317 if (MPD_ERROR(c))
318 return -1;
320 mpd_send_pause(c->connection, value);
321 return mpdclient_finish_command(c);
322 }
324 gint
325 mpdclient_cmd_crop(mpdclient_t *c)
326 {
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);
357 }
359 gint
360 mpdclient_cmd_stop(mpdclient_t *c)
361 {
362 if (MPD_ERROR(c))
363 return -1;
365 mpd_send_stop(c->connection);
366 return mpdclient_finish_command(c);
367 }
369 gint
370 mpdclient_cmd_next(mpdclient_t *c)
371 {
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);
378 }
380 gint
381 mpdclient_cmd_prev(mpdclient_t *c)
382 {
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);
389 }
391 gint
392 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
393 {
394 if (MPD_ERROR(c))
395 return -1;
397 mpd_send_seek_id(c->connection, id, pos);
398 return mpdclient_finish_command(c);
399 }
401 gint
402 mpdclient_cmd_shuffle(mpdclient_t *c)
403 {
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);
410 }
412 gint
413 mpdclient_cmd_shuffle_range(mpdclient_t *c, guint start, guint end)
414 {
415 mpd_send_shuffle_range(c->connection, start, end);
416 c->need_update = TRUE;
417 return mpdclient_finish_command(c);
418 }
420 gint
421 mpdclient_cmd_clear(mpdclient_t *c)
422 {
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;
434 }
436 gint
437 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
438 {
439 if (MPD_ERROR(c))
440 return -1;
442 mpd_send_repeat(c->connection, value);
443 return mpdclient_finish_command(c);
444 }
446 gint
447 mpdclient_cmd_random(mpdclient_t *c, gint value)
448 {
449 if (MPD_ERROR(c))
450 return -1;
452 mpd_send_random(c->connection, value);
453 return mpdclient_finish_command(c);
454 }
456 gint
457 mpdclient_cmd_single(mpdclient_t *c, gint value)
458 {
459 if (MPD_ERROR(c))
460 return -1;
462 mpd_send_single(c->connection, value);
463 return mpdclient_finish_command(c);
464 }
466 gint
467 mpdclient_cmd_consume(mpdclient_t *c, gint value)
468 {
469 if (MPD_ERROR(c))
470 return -1;
472 mpd_send_consume(c->connection, value);
473 return mpdclient_finish_command(c);
474 }
476 gint
477 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
478 {
479 if (MPD_ERROR(c))
480 return -1;
482 mpd_send_crossfade(c->connection, value);
483 return mpdclient_finish_command(c);
484 }
486 gint
487 mpdclient_cmd_db_update(mpdclient_t *c, const gchar *path)
488 {
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;
504 }
506 gint
507 mpdclient_cmd_volume(mpdclient_t *c, gint value)
508 {
509 if (MPD_ERROR(c))
510 return -1;
512 mpd_send_set_volume(c->connection, value);
513 return mpdclient_finish_command(c);
514 }
516 gint mpdclient_cmd_volume_up(struct mpdclient *c)
517 {
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);
532 }
534 gint mpdclient_cmd_volume_down(struct mpdclient *c)
535 {
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);
550 }
552 gint
553 mpdclient_cmd_add_path(mpdclient_t *c, const gchar *path_utf8)
554 {
555 if (MPD_ERROR(c))
556 return -1;
558 mpd_send_add(c->connection, path_utf8);
559 return mpdclient_finish_command(c);
560 }
562 gint
563 mpdclient_cmd_add(mpdclient_t *c, const struct mpd_song *song)
564 {
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;
592 }
594 gint
595 mpdclient_cmd_delete(mpdclient_t *c, gint idx)
596 {
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;
640 }
642 gint
643 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
644 {
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;
683 }
685 gint
686 mpdclient_cmd_save_playlist(mpdclient_t *c, const gchar *filename_utf8)
687 {
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;
697 }
699 gint
700 mpdclient_cmd_load_playlist(mpdclient_t *c, const gchar *filename_utf8)
701 {
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);
708 }
710 gint
711 mpdclient_cmd_delete_playlist(mpdclient_t *c, const gchar *filename_utf8)
712 {
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;
722 }
725 /****************************************************************************/
726 /*** Callback management functions ******************************************/
727 /****************************************************************************/
729 static void
730 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
731 {
732 while (list) {
733 mpdc_list_cb_t fn = list->data;
735 fn(c, event, data);
736 list = list->next;
737 }
738 }
740 void
741 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
742 {
743 do_list_callbacks(c, c->playlist_callbacks, event, data);
744 }
746 void
747 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
748 {
749 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
750 }
752 void
753 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
754 {
755 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
756 }
758 void
759 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
760 {
761 do_list_callbacks(c, c->browse_callbacks, event, data);
762 }
765 void
766 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
767 {
768 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
769 }
771 void
772 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
773 {
774 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
775 }
777 void
778 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
779 {
780 c->error_callbacks = g_list_append(c->error_callbacks, cb);
781 }
783 void
784 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
785 {
786 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
787 }
790 /****************************************************************************/
791 /*** Playlist management functions ******************************************/
792 /****************************************************************************/
794 /* update playlist */
795 gint
796 mpdclient_playlist_update(mpdclient_t *c)
797 {
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);
820 }
822 #ifdef ENABLE_PLCHANGES
824 /* update playlist (plchanges) */
825 gint
826 mpdclient_playlist_update_changes(mpdclient_t *c)
827 {
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;
866 }
868 #else
869 gint
870 mpdclient_playlist_update_changes(mpdclient_t *c)
871 {
872 return mpdclient_playlist_update(c);
873 }
874 #endif
877 /****************************************************************************/
878 /*** Filelist functions *****************************************************/
879 /****************************************************************************/
881 mpdclient_filelist_t *
882 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
883 {
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;
905 }
907 static struct filelist *
908 mpdclient_recv_filelist_response(struct mpdclient *c)
909 {
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;
924 }
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)
931 {
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);
941 }
943 int
944 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
945 {
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);
973 }
975 GList *
976 mpdclient_get_artists(mpdclient_t *c)
977 {
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;
997 }
999 GList *
1000 mpdclient_get_albums(mpdclient_t *c, const gchar *artist_utf8)
1001 {
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;
1025 }