a688162a1832640f0938051d4b1a08afe90e6176
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 /****************************************************************************/
105 /*** mpdclient functions ****************************************************/
106 /****************************************************************************/
108 static gint
109 mpdclient_handle_error(struct mpdclient *c)
110 {
111 enum mpd_error error = mpd_connection_get_error(c->connection);
113 assert(error != MPD_ERROR_SUCCESS);
115 if (error == MPD_ERROR_SERVER &&
116 mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION &&
117 screen_auth(c) == 0)
118 return 0;
120 if (error == MPD_ERROR_SERVER)
121 error = error | (mpd_connection_get_server_error(c->connection) << 8);
123 for (GList *list = c->error_callbacks; list != NULL;
124 list = list->next) {
125 mpdc_error_cb_t cb = list->data;
126 cb(c, error, mpd_connection_get_error_message(c->connection));
127 }
129 if (!mpd_connection_clear_error(c->connection))
130 mpdclient_disconnect(c);
132 return error;
133 }
135 gint
136 mpdclient_finish_command(struct mpdclient *c)
137 {
138 return mpd_response_finish(c->connection)
139 ? 0 : mpdclient_handle_error(c);
140 }
142 struct mpdclient *
143 mpdclient_new(void)
144 {
145 struct mpdclient *c;
147 c = g_new0(struct mpdclient, 1);
148 playlist_init(&c->playlist);
149 c->volume = MPD_STATUS_NO_VOLUME;
151 return c;
152 }
154 void
155 mpdclient_free(struct mpdclient *c)
156 {
157 mpdclient_disconnect(c);
159 mpdclient_playlist_free(&c->playlist);
161 g_list_free(c->error_callbacks);
162 g_list_free(c->playlist_callbacks);
163 g_list_free(c->browse_callbacks);
164 g_free(c);
165 }
167 gint
168 mpdclient_disconnect(struct mpdclient *c)
169 {
170 if (c->connection)
171 mpd_connection_free(c->connection);
172 c->connection = NULL;
174 if (c->status)
175 mpd_status_free(c->status);
176 c->status = NULL;
178 playlist_clear(&c->playlist);
180 if (c->song)
181 c->song = NULL;
183 return 0;
184 }
186 gint
187 mpdclient_connect(struct mpdclient *c,
188 const gchar *host,
189 gint port,
190 gfloat _timeout,
191 const gchar *password)
192 {
193 gint retval = 0;
195 /* close any open connection */
196 if( c->connection )
197 mpdclient_disconnect(c);
199 /* connect to MPD */
200 c->connection = mpd_connection_new(host, port, _timeout * 1000);
201 if (c->connection == NULL)
202 g_error("Out of memory");
204 if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
205 retval = mpdclient_handle_error(c);
206 if (retval != 0) {
207 mpd_connection_free(c->connection);
208 c->connection = NULL;
209 }
211 return retval;
212 }
214 /* send password */
215 if( password ) {
216 mpd_send_password(c->connection, password);
217 retval = mpdclient_finish_command(c);
218 }
219 c->need_update = TRUE;
221 return retval;
222 }
224 gint
225 mpdclient_update(struct mpdclient *c)
226 {
227 gint retval = 0;
229 c->volume = MPD_STATUS_NO_VOLUME;
231 if (MPD_ERROR(c))
232 return -1;
234 /* free the old status */
235 if (c->status)
236 mpd_status_free(c->status);
238 /* retrieve new status */
239 c->status = mpd_run_status(c->connection);
240 if (c->status == NULL)
241 return mpdclient_handle_error(c);
243 if (c->updatingdb &&
244 c->updatingdb != mpd_status_get_update_id(c->status))
245 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
247 c->updatingdb = mpd_status_get_update_id(c->status);
248 c->volume = mpd_status_get_volume(c->status);
250 /* check if the playlist needs an update */
251 if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
252 if (playlist_is_empty(&c->playlist))
253 retval = mpdclient_playlist_update_changes(c);
254 else
255 retval = mpdclient_playlist_update(c);
256 }
258 /* update the current song */
259 if (!c->song || mpd_status_get_song_id(c->status)) {
260 c->song = playlist_get_song(c, mpd_status_get_song_pos(c->status));
261 }
263 c->need_update = FALSE;
265 return retval;
266 }
269 /****************************************************************************/
270 /*** MPD Commands **********************************************************/
271 /****************************************************************************/
273 gint
274 mpdclient_cmd_play(struct mpdclient *c, gint idx)
275 {
276 #ifdef ENABLE_SONG_ID
277 struct mpd_song *song = playlist_get_song(c, idx);
279 if (MPD_ERROR(c))
280 return -1;
282 if (song)
283 mpd_send_play_id(c->connection, mpd_song_get_id(song));
284 else
285 mpd_send_play(c->connection);
286 #else
287 if (MPD_ERROR(c))
288 return -1;
290 mpd_sendPlayCommand(c->connection, idx);
291 #endif
292 c->need_update = TRUE;
293 return mpdclient_finish_command(c);
294 }
296 gint
297 mpdclient_cmd_pause(struct mpdclient *c, gint value)
298 {
299 if (MPD_ERROR(c))
300 return -1;
302 mpd_send_pause(c->connection, value);
303 return mpdclient_finish_command(c);
304 }
306 gint
307 mpdclient_cmd_crop(struct mpdclient *c)
308 {
309 struct mpd_status *status;
310 bool playing;
311 int length, current;
313 if (MPD_ERROR(c))
314 return -1;
316 status = mpd_run_status(c->connection);
317 if (status == NULL)
318 return mpdclient_handle_error(c);
320 playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
321 mpd_status_get_state(status) == MPD_STATE_PAUSE;
322 length = mpd_status_get_queue_length(status);
323 current = mpd_status_get_song_pos(status);
325 mpd_status_free(status);
327 if (!playing || length < 2)
328 return 0;
330 mpd_command_list_begin(c->connection, false);
332 while (--length >= 0)
333 if (length != current)
334 mpd_send_delete(c->connection, length);
336 mpd_command_list_end(c->connection);
338 return mpdclient_finish_command(c);
339 }
341 gint
342 mpdclient_cmd_stop(struct mpdclient *c)
343 {
344 if (MPD_ERROR(c))
345 return -1;
347 mpd_send_stop(c->connection);
348 return mpdclient_finish_command(c);
349 }
351 gint
352 mpdclient_cmd_next(struct mpdclient *c)
353 {
354 if (MPD_ERROR(c))
355 return -1;
357 mpd_send_next(c->connection);
358 c->need_update = TRUE;
359 return mpdclient_finish_command(c);
360 }
362 gint
363 mpdclient_cmd_prev(struct mpdclient *c)
364 {
365 if (MPD_ERROR(c))
366 return -1;
368 mpd_send_previous(c->connection);
369 c->need_update = TRUE;
370 return mpdclient_finish_command(c);
371 }
373 gint
374 mpdclient_cmd_seek(struct mpdclient *c, gint id, gint pos)
375 {
376 if (MPD_ERROR(c))
377 return -1;
379 mpd_send_seek_id(c->connection, id, pos);
380 return mpdclient_finish_command(c);
381 }
383 gint
384 mpdclient_cmd_shuffle(struct mpdclient *c)
385 {
386 if (MPD_ERROR(c))
387 return -1;
389 mpd_send_shuffle(c->connection);
390 c->need_update = TRUE;
391 return mpdclient_finish_command(c);
392 }
394 gint
395 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
396 {
397 mpd_send_shuffle_range(c->connection, start, end);
398 c->need_update = TRUE;
399 return mpdclient_finish_command(c);
400 }
402 gint
403 mpdclient_cmd_clear(struct mpdclient *c)
404 {
405 gint retval = 0;
407 if (MPD_ERROR(c))
408 return -1;
410 mpd_send_clear(c->connection);
411 retval = mpdclient_finish_command(c);
412 /* call playlist updated callback */
413 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
414 c->need_update = TRUE;
415 return retval;
416 }
418 gint
419 mpdclient_cmd_repeat(struct mpdclient *c, gint value)
420 {
421 if (MPD_ERROR(c))
422 return -1;
424 mpd_send_repeat(c->connection, value);
425 return mpdclient_finish_command(c);
426 }
428 gint
429 mpdclient_cmd_random(struct mpdclient *c, gint value)
430 {
431 if (MPD_ERROR(c))
432 return -1;
434 mpd_send_random(c->connection, value);
435 return mpdclient_finish_command(c);
436 }
438 gint
439 mpdclient_cmd_single(struct mpdclient *c, gint value)
440 {
441 if (MPD_ERROR(c))
442 return -1;
444 mpd_send_single(c->connection, value);
445 return mpdclient_finish_command(c);
446 }
448 gint
449 mpdclient_cmd_consume(struct mpdclient *c, gint value)
450 {
451 if (MPD_ERROR(c))
452 return -1;
454 mpd_send_consume(c->connection, value);
455 return mpdclient_finish_command(c);
456 }
458 gint
459 mpdclient_cmd_crossfade(struct mpdclient *c, gint value)
460 {
461 if (MPD_ERROR(c))
462 return -1;
464 mpd_send_crossfade(c->connection, value);
465 return mpdclient_finish_command(c);
466 }
468 gint
469 mpdclient_cmd_db_update(struct mpdclient *c, const gchar *path)
470 {
471 gint ret;
473 if (MPD_ERROR(c))
474 return -1;
476 mpd_send_update(c->connection, path ? path : "");
477 ret = mpdclient_finish_command(c);
479 if (ret == 0)
480 /* set updatingDb to make sure the browse callback
481 gets called even if the update has finished before
482 status is updated */
483 c->updatingdb = 1;
485 return ret;
486 }
488 gint
489 mpdclient_cmd_volume(struct mpdclient *c, gint value)
490 {
491 if (MPD_ERROR(c))
492 return -1;
494 mpd_send_set_volume(c->connection, value);
495 return mpdclient_finish_command(c);
496 }
498 gint mpdclient_cmd_volume_up(struct mpdclient *c)
499 {
500 if (MPD_ERROR(c))
501 return -1;
503 if (c->status == NULL ||
504 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
505 return 0;
507 if (c->volume == MPD_STATUS_NO_VOLUME)
508 c->volume = mpd_status_get_volume(c->status);
510 if (c->volume >= 100)
511 return 0;
513 return mpdclient_cmd_volume(c, ++c->volume);
514 }
516 gint mpdclient_cmd_volume_down(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 <= 0)
529 return 0;
531 return mpdclient_cmd_volume(c, --c->volume);
532 }
534 gint
535 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
536 {
537 if (MPD_ERROR(c))
538 return -1;
540 mpd_send_add(c->connection, path_utf8);
541 return mpdclient_finish_command(c);
542 }
544 gint
545 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
546 {
547 gint retval = 0;
549 if (MPD_ERROR(c))
550 return -1;
552 if (song == NULL)
553 return -1;
555 /* send the add command to mpd */
556 mpd_send_add(c->connection, mpd_song_get_uri(song));
557 if( (retval=mpdclient_finish_command(c)) )
558 return retval;
560 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
561 /* add the song to playlist */
562 playlist_append(&c->playlist, song);
564 /* increment the playlist id, so we don't retrieve a new playlist */
565 c->playlist.id++;
567 /* call playlist updated callback */
568 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
569 #else
570 c->need_update = TRUE;
571 #endif
573 return 0;
574 }
576 gint
577 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
578 {
579 gint retval = 0;
580 struct mpd_song *song;
582 if (MPD_ERROR(c))
583 return -1;
585 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
586 return -1;
588 song = playlist_get(&c->playlist, idx);
590 /* send the delete command to mpd */
591 #ifdef ENABLE_SONG_ID
592 mpd_send_delete_id(c->connection, mpd_song_get_id(song));
593 #else
594 mpd_send_delete(c->connection, idx);
595 #endif
596 if( (retval=mpdclient_finish_command(c)) )
597 return retval;
599 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
600 /* increment the playlist id, so we don't retrieve a new playlist */
601 c->playlist.id++;
603 /* remove the song from the playlist */
604 playlist_remove_reuse(&c->playlist, idx);
606 /* call playlist updated callback */
607 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
609 /* remove references to the song */
610 if (c->song == song) {
611 c->song = NULL;
612 c->need_update = TRUE;
613 }
615 mpd_song_free(song);
617 #else
618 c->need_update = TRUE;
619 #endif
621 return 0;
622 }
624 gint
625 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
626 {
627 gint n;
628 struct mpd_song *song1, *song2;
630 if (MPD_ERROR(c))
631 return -1;
633 if (old_index == new_index || new_index < 0 ||
634 (guint)new_index >= c->playlist.list->len)
635 return -1;
637 song1 = playlist_get(&c->playlist, old_index);
638 song2 = playlist_get(&c->playlist, new_index);
640 /* send the move command to mpd */
641 #ifdef ENABLE_SONG_ID
642 mpd_send_swap_id(c->connection,
643 mpd_song_get_id(song1), mpd_song_get_id(song2));
644 #else
645 mpd_send_move(c->connection, old_index, new_index);
646 #endif
647 if( (n=mpdclient_finish_command(c)) )
648 return n;
650 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
651 /* update the playlist */
652 playlist_swap(&c->playlist, old_index, new_index);
654 /* increment the playlist id, so we don't retrieve a new playlist */
655 c->playlist.id++;
657 #else
658 c->need_update = TRUE;
659 #endif
661 /* call playlist updated callback */
662 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
664 return 0;
665 }
667 gint
668 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
669 {
670 gint retval = 0;
672 if (MPD_ERROR(c))
673 return -1;
675 mpd_send_save(c->connection, filename_utf8);
676 if ((retval = mpdclient_finish_command(c)) == 0)
677 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
678 return retval;
679 }
681 gint
682 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
683 {
684 if (MPD_ERROR(c))
685 return -1;
687 mpd_send_load(c->connection, filename_utf8);
688 c->need_update = TRUE;
689 return mpdclient_finish_command(c);
690 }
692 gint
693 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
694 {
695 gint retval = 0;
697 if (MPD_ERROR(c))
698 return -1;
700 mpd_send_rm(c->connection, filename_utf8);
701 if ((retval = mpdclient_finish_command(c)) == 0)
702 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
703 return retval;
704 }
707 /****************************************************************************/
708 /*** Callback management functions ******************************************/
709 /****************************************************************************/
711 static void
712 do_list_callbacks(struct mpdclient *c, GList *list, gint event, gpointer data)
713 {
714 while (list) {
715 mpdc_list_cb_t fn = list->data;
717 fn(c, event, data);
718 list = list->next;
719 }
720 }
722 void
723 mpdclient_playlist_callback(struct mpdclient *c, int event, gpointer data)
724 {
725 do_list_callbacks(c, c->playlist_callbacks, event, data);
726 }
728 void
729 mpdclient_install_playlist_callback(struct mpdclient *c,mpdc_list_cb_t cb)
730 {
731 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
732 }
734 void
735 mpdclient_remove_playlist_callback(struct mpdclient *c, mpdc_list_cb_t cb)
736 {
737 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
738 }
740 void
741 mpdclient_browse_callback(struct mpdclient *c, int event, gpointer data)
742 {
743 do_list_callbacks(c, c->browse_callbacks, event, data);
744 }
747 void
748 mpdclient_install_browse_callback(struct mpdclient *c,mpdc_list_cb_t cb)
749 {
750 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
751 }
753 void
754 mpdclient_remove_browse_callback(struct mpdclient *c, mpdc_list_cb_t cb)
755 {
756 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
757 }
759 void
760 mpdclient_install_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
761 {
762 c->error_callbacks = g_list_append(c->error_callbacks, cb);
763 }
765 void
766 mpdclient_remove_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
767 {
768 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
769 }
772 /****************************************************************************/
773 /*** Playlist management functions ******************************************/
774 /****************************************************************************/
776 /* update playlist */
777 gint
778 mpdclient_playlist_update(struct mpdclient *c)
779 {
780 struct mpd_entity *entity;
782 if (MPD_ERROR(c))
783 return -1;
785 playlist_clear(&c->playlist);
787 mpd_send_list_queue_meta(c->connection);
788 while ((entity = mpd_recv_entity(c->connection))) {
789 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
790 playlist_append(&c->playlist, mpd_entity_get_song(entity));
792 mpd_entity_free(entity);
793 }
795 c->playlist.id = mpd_status_get_queue_version(c->status);
796 c->song = NULL;
798 /* call playlist updated callbacks */
799 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
801 return mpdclient_finish_command(c);
802 }
804 #ifdef ENABLE_PLCHANGES
806 /* update playlist (plchanges) */
807 gint
808 mpdclient_playlist_update_changes(struct mpdclient *c)
809 {
810 struct mpd_song *song;
811 guint length;
813 if (MPD_ERROR(c))
814 return -1;
816 mpd_send_queue_changes_meta(c->connection, c->playlist.id);
818 while ((song = mpd_recv_song(c->connection)) != NULL) {
819 int pos = mpd_song_get_pos(song);
821 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
822 /* update song */
823 playlist_replace(&c->playlist, pos, song);
824 } else {
825 /* add a new song */
826 playlist_append(&c->playlist, song);
827 }
829 mpd_song_free(song);
830 }
832 /* remove trailing songs */
834 length = mpd_status_get_queue_length(c->status);
835 while (length < c->playlist.list->len) {
836 guint pos = c->playlist.list->len - 1;
838 /* Remove the last playlist entry */
839 playlist_remove(&c->playlist, pos);
840 }
842 c->song = NULL;
843 c->playlist.id = mpd_status_get_queue_version(c->status);
845 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
847 return 0;
848 }
850 #else
851 gint
852 mpdclient_playlist_update_changes(struct mpdclient *c)
853 {
854 return mpdclient_playlist_update(c);
855 }
856 #endif
859 /****************************************************************************/
860 /*** Filelist functions *****************************************************/
861 /****************************************************************************/
863 struct filelist *
864 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
865 {
866 struct filelist *filelist;
867 struct mpd_entity *entity;
869 if (MPD_ERROR(c))
870 return NULL;
872 mpd_send_list_meta(c->connection, path);
873 filelist = filelist_new();
874 if (path && path[0] && strcmp(path, "/"))
875 /* add a dummy entry for ./.. */
876 filelist_append(filelist, NULL);
878 while ((entity = mpd_recv_entity(c->connection)) != NULL)
879 filelist_append(filelist, entity);
881 /* If there's an error, ignore it. We'll return an empty filelist. */
882 mpdclient_finish_command(c);
884 filelist_sort_dir_play(filelist, compare_filelistentry);
886 return filelist;
887 }
889 static struct filelist *
890 mpdclient_recv_filelist_response(struct mpdclient *c)
891 {
892 struct filelist *filelist;
893 struct mpd_entity *entity;
895 filelist = filelist_new();
897 while ((entity = mpd_recv_entity(c->connection)) != NULL)
898 filelist_append(filelist, entity);
900 if (mpdclient_finish_command(c)) {
901 filelist_free(filelist);
902 return NULL;
903 }
905 return filelist;
906 }
908 struct filelist *
909 mpdclient_filelist_search(struct mpdclient *c,
910 int exact_match,
911 enum mpd_tag_type tag,
912 gchar *filter_utf8)
913 {
914 if (MPD_ERROR(c))
915 return NULL;
917 mpd_search_db_songs(c->connection, exact_match);
918 mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
919 tag, filter_utf8);
920 mpd_search_commit(c->connection);
922 return mpdclient_recv_filelist_response(c);
923 }
925 int
926 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
927 {
928 guint i;
930 if (MPD_ERROR(c))
931 return -1;
933 if (filelist_is_empty(fl))
934 return 0;
936 mpd_command_list_begin(c->connection, false);
938 for (i = 0; i < filelist_length(fl); ++i) {
939 struct filelist_entry *entry = filelist_get(fl, i);
940 struct mpd_entity *entity = entry->entity;
942 if (entity != NULL &&
943 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
944 const struct mpd_song *song =
945 mpd_entity_get_song(entity);
946 const char *uri = mpd_song_get_uri(song);
948 if (uri != NULL)
949 mpd_send_add(c->connection, uri);
950 }
951 }
953 mpd_command_list_end(c->connection);
954 return mpdclient_finish_command(c);
955 }
957 GList *
958 mpdclient_get_artists(struct mpdclient *c)
959 {
960 GList *list = NULL;
961 struct mpd_pair *pair;
963 if (MPD_ERROR(c))
964 return NULL;
966 mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
967 mpd_search_commit(c->connection);
969 while ((pair = mpd_recv_pair_tag(c->connection,
970 MPD_TAG_ARTIST)) != NULL) {
971 list = g_list_append(list, g_strdup(pair->value));
972 mpd_return_pair(c->connection, pair);
973 }
975 if (mpdclient_finish_command(c))
976 return string_list_free(list);
978 return list;
979 }
981 GList *
982 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
983 {
984 GList *list = NULL;
985 struct mpd_pair *pair;
987 if (MPD_ERROR(c))
988 return NULL;
990 mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
991 if (artist_utf8 != NULL)
992 mpd_search_add_tag_constraint(c->connection,
993 MPD_OPERATOR_DEFAULT,
994 MPD_TAG_ARTIST, artist_utf8);
995 mpd_search_commit(c->connection);
997 while ((pair = mpd_recv_pair_tag(c->connection,
998 MPD_TAG_ALBUM)) != NULL) {
999 list = g_list_append(list, g_strdup(pair->value));
1000 mpd_return_pair(c->connection, pair);
1001 }
1003 if (mpdclient_finish_command(c))
1004 return string_list_free(list);
1006 return list;
1007 }