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_utils.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
38 #define ENABLE_SONG_ID
39 #define ENABLE_PLCHANGES
41 #define BUFSIZE 1024
43 static bool
44 MPD_ERROR(const struct mpdclient *client)
45 {
46 return client->connection == NULL ||
47 mpd_connection_get_error(client->connection) != MPD_ERROR_SUCCESS;
48 }
50 /* filelist sorting functions */
51 static gint
52 compare_filelistentry(gconstpointer filelist_entry1,
53 gconstpointer filelist_entry2)
54 {
55 const struct mpd_entity *e1, *e2;
56 int n = 0;
58 e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
59 e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
61 if (e1 != NULL && e2 != NULL &&
62 mpd_entity_get_type(e1) == mpd_entity_get_type(e2)) {
63 switch (mpd_entity_get_type(e1)) {
64 case MPD_ENTITY_TYPE_UNKNOWN:
65 break;
66 case MPD_ENTITY_TYPE_DIRECTORY:
67 n = g_utf8_collate(mpd_directory_get_path(mpd_entity_get_directory(e1)),
68 mpd_directory_get_path(mpd_entity_get_directory(e2)));
69 break;
70 case MPD_ENTITY_TYPE_SONG:
71 break;
72 case MPD_ENTITY_TYPE_PLAYLIST:
73 n = g_utf8_collate(mpd_playlist_get_path(mpd_entity_get_playlist(e1)),
74 mpd_playlist_get_path(mpd_entity_get_playlist(e2)));
75 }
76 }
77 return n;
78 }
80 /* sort by list-format */
81 gint
82 compare_filelistentry_format(gconstpointer filelist_entry1,
83 gconstpointer filelist_entry2)
84 {
85 const struct mpd_entity *e1, *e2;
86 char key1[BUFSIZE], key2[BUFSIZE];
87 int n = 0;
89 e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
90 e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
92 if (e1 && e2 &&
93 mpd_entity_get_type(e1) == MPD_ENTITY_TYPE_SONG &&
94 mpd_entity_get_type(e2) == MPD_ENTITY_TYPE_SONG) {
95 strfsong(key1, BUFSIZE, options.list_format, mpd_entity_get_song(e1));
96 strfsong(key2, BUFSIZE, options.list_format, mpd_entity_get_song(e2));
97 n = strcmp(key1,key2);
98 }
100 return n;
101 }
104 /* Error callbacks */
105 static gint
106 error_cb(struct mpdclient *c, gint error, const gchar *msg)
107 {
108 GList *list = c->error_callbacks;
110 if (list == NULL)
111 fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
113 while (list) {
114 mpdc_error_cb_t cb = list->data;
115 if (cb)
116 cb(c, error, msg);
117 list = list->next;
118 }
120 mpd_connection_clear_error(c->connection);
121 return error;
122 }
125 /****************************************************************************/
126 /*** mpdclient functions ****************************************************/
127 /****************************************************************************/
129 static gint
130 mpdclient_handle_error(struct mpdclient *c)
131 {
132 enum mpd_error error = mpd_connection_get_error(c->connection);
133 bool is_fatal = error != MPD_ERROR_SERVER;
135 assert(error != MPD_ERROR_SUCCESS);
137 if (error == MPD_ERROR_SERVER &&
138 mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION &&
139 screen_auth(c) == 0)
140 return 0;
142 if (error == MPD_ERROR_SERVER)
143 error = error | (mpd_connection_get_server_error(c->connection) << 8);
145 error_cb(c, error, mpd_connection_get_error_message(c->connection));
147 if (is_fatal)
148 mpdclient_disconnect(c);
150 return error;
151 }
153 gint
154 mpdclient_finish_command(struct mpdclient *c)
155 {
156 return mpd_response_finish(c->connection)
157 ? 0 : mpdclient_handle_error(c);
158 }
160 struct mpdclient *
161 mpdclient_new(void)
162 {
163 struct mpdclient *c;
165 c = g_new0(struct mpdclient, 1);
166 playlist_init(&c->playlist);
167 c->volume = MPD_STATUS_NO_VOLUME;
169 return c;
170 }
172 void
173 mpdclient_free(struct mpdclient *c)
174 {
175 mpdclient_disconnect(c);
177 mpdclient_playlist_free(&c->playlist);
179 g_list_free(c->error_callbacks);
180 g_list_free(c->playlist_callbacks);
181 g_list_free(c->browse_callbacks);
182 g_free(c);
183 }
185 gint
186 mpdclient_disconnect(struct mpdclient *c)
187 {
188 if (c->connection)
189 mpd_connection_free(c->connection);
190 c->connection = NULL;
192 if (c->status)
193 mpd_status_free(c->status);
194 c->status = NULL;
196 playlist_clear(&c->playlist);
198 if (c->song)
199 c->song = NULL;
201 return 0;
202 }
204 gint
205 mpdclient_connect(struct mpdclient *c,
206 const gchar *host,
207 gint port,
208 gfloat _timeout,
209 const gchar *password)
210 {
211 gint retval = 0;
213 /* close any open connection */
214 if( c->connection )
215 mpdclient_disconnect(c);
217 /* connect to MPD */
218 c->connection = mpd_connection_new(host, port, _timeout * 1000);
219 if (c->connection == NULL)
220 g_error("Out of memory");
222 if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
223 retval = error_cb(c, mpd_connection_get_error(c->connection),
224 mpd_connection_get_error_message(c->connection));
225 if (retval != 0) {
226 mpd_connection_free(c->connection);
227 c->connection = NULL;
228 }
230 return retval;
231 }
233 /* send password */
234 if( password ) {
235 mpd_send_password(c->connection, password);
236 retval = mpdclient_finish_command(c);
237 }
238 c->need_update = TRUE;
240 return retval;
241 }
243 gint
244 mpdclient_update(struct mpdclient *c)
245 {
246 gint retval = 0;
248 c->volume = MPD_STATUS_NO_VOLUME;
250 if (MPD_ERROR(c))
251 return -1;
253 /* free the old status */
254 if (c->status)
255 mpd_status_free(c->status);
257 /* retrieve new status */
258 c->status = mpd_run_status(c->connection);
259 if (c->status == NULL)
260 return mpdclient_handle_error(c);
262 if (c->updatingdb &&
263 c->updatingdb != mpd_status_get_update_id(c->status))
264 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
266 c->updatingdb = mpd_status_get_update_id(c->status);
267 c->volume = mpd_status_get_volume(c->status);
269 /* check if the playlist needs an update */
270 if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
271 if (playlist_is_empty(&c->playlist))
272 retval = mpdclient_playlist_update_changes(c);
273 else
274 retval = mpdclient_playlist_update(c);
275 }
277 /* update the current song */
278 if (!c->song || mpd_status_get_song_id(c->status)) {
279 c->song = playlist_get_song(c, mpd_status_get_song_pos(c->status));
280 }
282 c->need_update = FALSE;
284 return retval;
285 }
288 /****************************************************************************/
289 /*** MPD Commands **********************************************************/
290 /****************************************************************************/
292 gint
293 mpdclient_cmd_play(struct mpdclient *c, gint idx)
294 {
295 #ifdef ENABLE_SONG_ID
296 struct mpd_song *song = playlist_get_song(c, idx);
298 if (MPD_ERROR(c))
299 return -1;
301 if (song)
302 mpd_send_play_id(c->connection, mpd_song_get_id(song));
303 else
304 mpd_send_play(c->connection);
305 #else
306 if (MPD_ERROR(c))
307 return -1;
309 mpd_sendPlayCommand(c->connection, idx);
310 #endif
311 c->need_update = TRUE;
312 return mpdclient_finish_command(c);
313 }
315 gint
316 mpdclient_cmd_pause(struct mpdclient *c, gint value)
317 {
318 if (MPD_ERROR(c))
319 return -1;
321 mpd_send_pause(c->connection, value);
322 return mpdclient_finish_command(c);
323 }
325 gint
326 mpdclient_cmd_crop(struct mpdclient *c)
327 {
328 struct mpd_status *status;
329 bool playing;
330 int length, current;
332 if (MPD_ERROR(c))
333 return -1;
335 status = mpd_run_status(c->connection);
336 if (status == NULL)
337 return mpdclient_handle_error(c);
339 playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
340 mpd_status_get_state(status) == MPD_STATE_PAUSE;
341 length = mpd_status_get_queue_length(status);
342 current = mpd_status_get_song_pos(status);
344 mpd_status_free(status);
346 if (!playing || length < 2)
347 return 0;
349 mpd_command_list_begin(c->connection, false);
351 while (--length >= 0)
352 if (length != current)
353 mpd_send_delete(c->connection, length);
355 mpd_command_list_end(c->connection);
357 return mpdclient_finish_command(c);
358 }
360 gint
361 mpdclient_cmd_stop(struct mpdclient *c)
362 {
363 if (MPD_ERROR(c))
364 return -1;
366 mpd_send_stop(c->connection);
367 return mpdclient_finish_command(c);
368 }
370 gint
371 mpdclient_cmd_next(struct mpdclient *c)
372 {
373 if (MPD_ERROR(c))
374 return -1;
376 mpd_send_next(c->connection);
377 c->need_update = TRUE;
378 return mpdclient_finish_command(c);
379 }
381 gint
382 mpdclient_cmd_prev(struct mpdclient *c)
383 {
384 if (MPD_ERROR(c))
385 return -1;
387 mpd_send_previous(c->connection);
388 c->need_update = TRUE;
389 return mpdclient_finish_command(c);
390 }
392 gint
393 mpdclient_cmd_seek(struct mpdclient *c, gint id, gint pos)
394 {
395 if (MPD_ERROR(c))
396 return -1;
398 mpd_send_seek_id(c->connection, id, pos);
399 return mpdclient_finish_command(c);
400 }
402 gint
403 mpdclient_cmd_shuffle(struct mpdclient *c)
404 {
405 if (MPD_ERROR(c))
406 return -1;
408 mpd_send_shuffle(c->connection);
409 c->need_update = TRUE;
410 return mpdclient_finish_command(c);
411 }
413 gint
414 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
415 {
416 mpd_send_shuffle_range(c->connection, start, end);
417 c->need_update = TRUE;
418 return mpdclient_finish_command(c);
419 }
421 gint
422 mpdclient_cmd_clear(struct mpdclient *c)
423 {
424 gint retval = 0;
426 if (MPD_ERROR(c))
427 return -1;
429 mpd_send_clear(c->connection);
430 retval = mpdclient_finish_command(c);
431 /* call playlist updated callback */
432 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
433 c->need_update = TRUE;
434 return retval;
435 }
437 gint
438 mpdclient_cmd_repeat(struct mpdclient *c, gint value)
439 {
440 if (MPD_ERROR(c))
441 return -1;
443 mpd_send_repeat(c->connection, value);
444 return mpdclient_finish_command(c);
445 }
447 gint
448 mpdclient_cmd_random(struct mpdclient *c, gint value)
449 {
450 if (MPD_ERROR(c))
451 return -1;
453 mpd_send_random(c->connection, value);
454 return mpdclient_finish_command(c);
455 }
457 gint
458 mpdclient_cmd_single(struct mpdclient *c, gint value)
459 {
460 if (MPD_ERROR(c))
461 return -1;
463 mpd_send_single(c->connection, value);
464 return mpdclient_finish_command(c);
465 }
467 gint
468 mpdclient_cmd_consume(struct mpdclient *c, gint value)
469 {
470 if (MPD_ERROR(c))
471 return -1;
473 mpd_send_consume(c->connection, value);
474 return mpdclient_finish_command(c);
475 }
477 gint
478 mpdclient_cmd_crossfade(struct mpdclient *c, gint value)
479 {
480 if (MPD_ERROR(c))
481 return -1;
483 mpd_send_crossfade(c->connection, value);
484 return mpdclient_finish_command(c);
485 }
487 gint
488 mpdclient_cmd_db_update(struct mpdclient *c, const gchar *path)
489 {
490 gint ret;
492 if (MPD_ERROR(c))
493 return -1;
495 mpd_send_update(c->connection, path ? path : "");
496 ret = mpdclient_finish_command(c);
498 if (ret == 0)
499 /* set updatingDb to make sure the browse callback
500 gets called even if the update has finished before
501 status is updated */
502 c->updatingdb = 1;
504 return ret;
505 }
507 gint
508 mpdclient_cmd_volume(struct mpdclient *c, gint value)
509 {
510 if (MPD_ERROR(c))
511 return -1;
513 mpd_send_set_volume(c->connection, value);
514 return mpdclient_finish_command(c);
515 }
517 gint mpdclient_cmd_volume_up(struct mpdclient *c)
518 {
519 if (MPD_ERROR(c))
520 return -1;
522 if (c->status == NULL ||
523 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
524 return 0;
526 if (c->volume == MPD_STATUS_NO_VOLUME)
527 c->volume = mpd_status_get_volume(c->status);
529 if (c->volume >= 100)
530 return 0;
532 return mpdclient_cmd_volume(c, ++c->volume);
533 }
535 gint mpdclient_cmd_volume_down(struct mpdclient *c)
536 {
537 if (MPD_ERROR(c))
538 return -1;
540 if (c->status == NULL ||
541 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
542 return 0;
544 if (c->volume == MPD_STATUS_NO_VOLUME)
545 c->volume = mpd_status_get_volume(c->status);
547 if (c->volume <= 0)
548 return 0;
550 return mpdclient_cmd_volume(c, --c->volume);
551 }
553 gint
554 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
555 {
556 if (MPD_ERROR(c))
557 return -1;
559 mpd_send_add(c->connection, path_utf8);
560 return mpdclient_finish_command(c);
561 }
563 gint
564 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
565 {
566 gint retval = 0;
568 if (MPD_ERROR(c))
569 return -1;
571 if (song == NULL)
572 return -1;
574 /* send the add command to mpd */
575 mpd_send_add(c->connection, mpd_song_get_uri(song));
576 if( (retval=mpdclient_finish_command(c)) )
577 return retval;
579 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
580 /* add the song to playlist */
581 playlist_append(&c->playlist, song);
583 /* increment the playlist id, so we don't retrieve a new playlist */
584 c->playlist.id++;
586 /* call playlist updated callback */
587 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
588 #else
589 c->need_update = TRUE;
590 #endif
592 return 0;
593 }
595 gint
596 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
597 {
598 gint retval = 0;
599 struct mpd_song *song;
601 if (MPD_ERROR(c))
602 return -1;
604 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
605 return -1;
607 song = playlist_get(&c->playlist, idx);
609 /* send the delete command to mpd */
610 #ifdef ENABLE_SONG_ID
611 mpd_send_delete_id(c->connection, mpd_song_get_id(song));
612 #else
613 mpd_send_delete(c->connection, idx);
614 #endif
615 if( (retval=mpdclient_finish_command(c)) )
616 return retval;
618 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
619 /* increment the playlist id, so we don't retrieve a new playlist */
620 c->playlist.id++;
622 /* remove the song from the playlist */
623 playlist_remove_reuse(&c->playlist, idx);
625 /* call playlist updated callback */
626 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
628 /* remove references to the song */
629 if (c->song == song) {
630 c->song = NULL;
631 c->need_update = TRUE;
632 }
634 mpd_song_free(song);
636 #else
637 c->need_update = TRUE;
638 #endif
640 return 0;
641 }
643 gint
644 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
645 {
646 gint n;
647 struct mpd_song *song1, *song2;
649 if (MPD_ERROR(c))
650 return -1;
652 if (old_index == new_index || new_index < 0 ||
653 (guint)new_index >= c->playlist.list->len)
654 return -1;
656 song1 = playlist_get(&c->playlist, old_index);
657 song2 = playlist_get(&c->playlist, new_index);
659 /* send the move command to mpd */
660 #ifdef ENABLE_SONG_ID
661 mpd_send_swap_id(c->connection,
662 mpd_song_get_id(song1), mpd_song_get_id(song2));
663 #else
664 mpd_send_move(c->connection, old_index, new_index);
665 #endif
666 if( (n=mpdclient_finish_command(c)) )
667 return n;
669 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
670 /* update the playlist */
671 playlist_swap(&c->playlist, old_index, new_index);
673 /* increment the playlist id, so we don't retrieve a new playlist */
674 c->playlist.id++;
676 #else
677 c->need_update = TRUE;
678 #endif
680 /* call playlist updated callback */
681 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
683 return 0;
684 }
686 gint
687 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
688 {
689 gint retval = 0;
691 if (MPD_ERROR(c))
692 return -1;
694 mpd_send_save(c->connection, filename_utf8);
695 if ((retval = mpdclient_finish_command(c)) == 0)
696 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
697 return retval;
698 }
700 gint
701 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
702 {
703 if (MPD_ERROR(c))
704 return -1;
706 mpd_send_load(c->connection, filename_utf8);
707 c->need_update = TRUE;
708 return mpdclient_finish_command(c);
709 }
711 gint
712 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
713 {
714 gint retval = 0;
716 if (MPD_ERROR(c))
717 return -1;
719 mpd_send_rm(c->connection, filename_utf8);
720 if ((retval = mpdclient_finish_command(c)) == 0)
721 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
722 return retval;
723 }
726 /****************************************************************************/
727 /*** Callback management functions ******************************************/
728 /****************************************************************************/
730 static void
731 do_list_callbacks(struct mpdclient *c, GList *list, gint event, gpointer data)
732 {
733 while (list) {
734 mpdc_list_cb_t fn = list->data;
736 fn(c, event, data);
737 list = list->next;
738 }
739 }
741 void
742 mpdclient_playlist_callback(struct mpdclient *c, int event, gpointer data)
743 {
744 do_list_callbacks(c, c->playlist_callbacks, event, data);
745 }
747 void
748 mpdclient_install_playlist_callback(struct mpdclient *c,mpdc_list_cb_t cb)
749 {
750 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
751 }
753 void
754 mpdclient_remove_playlist_callback(struct mpdclient *c, mpdc_list_cb_t cb)
755 {
756 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
757 }
759 void
760 mpdclient_browse_callback(struct mpdclient *c, int event, gpointer data)
761 {
762 do_list_callbacks(c, c->browse_callbacks, event, data);
763 }
766 void
767 mpdclient_install_browse_callback(struct mpdclient *c,mpdc_list_cb_t cb)
768 {
769 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
770 }
772 void
773 mpdclient_remove_browse_callback(struct mpdclient *c, mpdc_list_cb_t cb)
774 {
775 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
776 }
778 void
779 mpdclient_install_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
780 {
781 c->error_callbacks = g_list_append(c->error_callbacks, cb);
782 }
784 void
785 mpdclient_remove_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
786 {
787 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
788 }
791 /****************************************************************************/
792 /*** Playlist management functions ******************************************/
793 /****************************************************************************/
795 /* update playlist */
796 gint
797 mpdclient_playlist_update(struct mpdclient *c)
798 {
799 struct mpd_entity *entity;
801 if (MPD_ERROR(c))
802 return -1;
804 playlist_clear(&c->playlist);
806 mpd_send_list_queue_meta(c->connection);
807 while ((entity = mpd_recv_entity(c->connection))) {
808 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
809 playlist_append(&c->playlist, mpd_entity_get_song(entity));
811 mpd_entity_free(entity);
812 }
814 c->playlist.id = mpd_status_get_queue_version(c->status);
815 c->song = NULL;
817 /* call playlist updated callbacks */
818 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
820 return mpdclient_finish_command(c);
821 }
823 #ifdef ENABLE_PLCHANGES
825 /* update playlist (plchanges) */
826 gint
827 mpdclient_playlist_update_changes(struct mpdclient *c)
828 {
829 struct mpd_song *song;
830 guint length;
832 if (MPD_ERROR(c))
833 return -1;
835 mpd_send_queue_changes_meta(c->connection, c->playlist.id);
837 while ((song = mpd_recv_song(c->connection)) != NULL) {
838 int pos = mpd_song_get_pos(song);
840 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
841 /* update song */
842 playlist_replace(&c->playlist, pos, song);
843 } else {
844 /* add a new song */
845 playlist_append(&c->playlist, song);
846 }
848 mpd_song_free(song);
849 }
851 /* remove trailing songs */
853 length = mpd_status_get_queue_length(c->status);
854 while (length < c->playlist.list->len) {
855 guint pos = c->playlist.list->len - 1;
857 /* Remove the last playlist entry */
858 playlist_remove(&c->playlist, pos);
859 }
861 c->song = NULL;
862 c->playlist.id = mpd_status_get_queue_version(c->status);
864 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
866 return 0;
867 }
869 #else
870 gint
871 mpdclient_playlist_update_changes(struct mpdclient *c)
872 {
873 return mpdclient_playlist_update(c);
874 }
875 #endif
878 /****************************************************************************/
879 /*** Filelist functions *****************************************************/
880 /****************************************************************************/
882 struct filelist *
883 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
884 {
885 struct filelist *filelist;
886 struct mpd_entity *entity;
888 if (MPD_ERROR(c))
889 return NULL;
891 mpd_send_list_meta(c->connection, path);
892 filelist = filelist_new();
893 if (path && path[0] && strcmp(path, "/"))
894 /* add a dummy entry for ./.. */
895 filelist_append(filelist, NULL);
897 while ((entity = mpd_recv_entity(c->connection)) != NULL)
898 filelist_append(filelist, entity);
900 /* If there's an error, ignore it. We'll return an empty filelist. */
901 mpdclient_finish_command(c);
903 filelist_sort_dir_play(filelist, compare_filelistentry);
905 return filelist;
906 }
908 static struct filelist *
909 mpdclient_recv_filelist_response(struct mpdclient *c)
910 {
911 struct filelist *filelist;
912 struct mpd_entity *entity;
914 filelist = filelist_new();
916 while ((entity = mpd_recv_entity(c->connection)) != NULL)
917 filelist_append(filelist, entity);
919 if (mpdclient_finish_command(c)) {
920 filelist_free(filelist);
921 return NULL;
922 }
924 return filelist;
925 }
927 struct filelist *
928 mpdclient_filelist_search(struct mpdclient *c,
929 int exact_match,
930 enum mpd_tag_type tag,
931 gchar *filter_utf8)
932 {
933 if (MPD_ERROR(c))
934 return NULL;
936 mpd_search_db_songs(c->connection, exact_match);
937 mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
938 tag, filter_utf8);
939 mpd_search_commit(c->connection);
941 return mpdclient_recv_filelist_response(c);
942 }
944 int
945 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
946 {
947 guint i;
949 if (MPD_ERROR(c))
950 return -1;
952 if (filelist_is_empty(fl))
953 return 0;
955 mpd_command_list_begin(c->connection, false);
957 for (i = 0; i < filelist_length(fl); ++i) {
958 struct filelist_entry *entry = filelist_get(fl, i);
959 struct mpd_entity *entity = entry->entity;
961 if (entity != NULL &&
962 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
963 const struct mpd_song *song =
964 mpd_entity_get_song(entity);
965 const char *uri = mpd_song_get_uri(song);
967 if (uri != NULL)
968 mpd_send_add(c->connection, uri);
969 }
970 }
972 mpd_command_list_end(c->connection);
973 return mpdclient_finish_command(c);
974 }
976 GList *
977 mpdclient_get_artists(struct mpdclient *c)
978 {
979 GList *list = NULL;
980 struct mpd_pair *pair;
982 if (MPD_ERROR(c))
983 return NULL;
985 mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
986 mpd_search_commit(c->connection);
988 while ((pair = mpd_recv_pair_tag(c->connection,
989 MPD_TAG_ARTIST)) != NULL) {
990 list = g_list_append(list, g_strdup(pair->value));
991 mpd_return_pair(c->connection, pair);
992 }
994 if (mpdclient_finish_command(c))
995 return string_list_free(list);
997 return list;
998 }
1000 GList *
1001 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
1002 {
1003 GList *list = NULL;
1004 struct mpd_pair *pair;
1006 if (MPD_ERROR(c))
1007 return NULL;
1009 mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
1010 if (artist_utf8 != NULL)
1011 mpd_search_add_tag_constraint(c->connection,
1012 MPD_OPERATOR_DEFAULT,
1013 MPD_TAG_ARTIST, artist_utf8);
1014 mpd_search_commit(c->connection);
1016 while ((pair = mpd_recv_pair_tag(c->connection,
1017 MPD_TAG_ALBUM)) != NULL) {
1018 list = g_list_append(list, g_strdup(pair->value));
1019 mpd_return_pair(c->connection, pair);
1020 }
1022 if (mpdclient_finish_command(c))
1023 return string_list_free(list);
1025 return list;
1026 }