1 /* ncmpc (Ncurses MPD Client)
2 * (c) 2004-2009 The Music Player Daemon Project
3 * Project homepage: http://musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
20 #include "mpdclient.h"
21 #include "filelist.h"
22 #include "screen_client.h"
23 #include "config.h"
24 #include "options.h"
25 #include "strfsong.h"
26 #include "utils.h"
28 #include <mpd/client.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <string.h>
35 #undef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
36 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
37 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
39 #define BUFSIZE 1024
41 static bool
42 MPD_ERROR(const struct mpdclient *client)
43 {
44 return client->connection == NULL ||
45 mpd_connection_get_error(client->connection) != MPD_ERROR_SUCCESS;
46 }
48 /* filelist sorting functions */
49 static gint
50 compare_filelistentry(gconstpointer filelist_entry1,
51 gconstpointer filelist_entry2)
52 {
53 const struct mpd_entity *e1, *e2;
54 int n = 0;
56 e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
57 e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
59 if (e1 != NULL && e2 != NULL &&
60 mpd_entity_get_type(e1) == mpd_entity_get_type(e2)) {
61 switch (mpd_entity_get_type(e1)) {
62 case MPD_ENTITY_TYPE_UNKNOWN:
63 break;
64 case MPD_ENTITY_TYPE_DIRECTORY:
65 n = g_utf8_collate(mpd_directory_get_path(mpd_entity_get_directory(e1)),
66 mpd_directory_get_path(mpd_entity_get_directory(e2)));
67 break;
68 case MPD_ENTITY_TYPE_SONG:
69 break;
70 case MPD_ENTITY_TYPE_PLAYLIST:
71 n = g_utf8_collate(mpd_playlist_get_path(mpd_entity_get_playlist(e1)),
72 mpd_playlist_get_path(mpd_entity_get_playlist(e2)));
73 }
74 }
75 return n;
76 }
78 /* sort by list-format */
79 gint
80 compare_filelistentry_format(gconstpointer filelist_entry1,
81 gconstpointer filelist_entry2)
82 {
83 const struct mpd_entity *e1, *e2;
84 char key1[BUFSIZE], key2[BUFSIZE];
85 int n = 0;
87 e1 = ((const struct filelist_entry *)filelist_entry1)->entity;
88 e2 = ((const struct filelist_entry *)filelist_entry2)->entity;
90 if (e1 && e2 &&
91 mpd_entity_get_type(e1) == MPD_ENTITY_TYPE_SONG &&
92 mpd_entity_get_type(e2) == MPD_ENTITY_TYPE_SONG) {
93 strfsong(key1, BUFSIZE, options.list_format, mpd_entity_get_song(e1));
94 strfsong(key2, BUFSIZE, options.list_format, mpd_entity_get_song(e2));
95 n = strcmp(key1,key2);
96 }
98 return n;
99 }
102 /****************************************************************************/
103 /*** mpdclient functions ****************************************************/
104 /****************************************************************************/
106 gint
107 mpdclient_handle_error(struct mpdclient *c)
108 {
109 enum mpd_error error = mpd_connection_get_error(c->connection);
111 assert(error != MPD_ERROR_SUCCESS);
113 if (error == MPD_ERROR_SERVER &&
114 mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION &&
115 screen_auth(c))
116 return 0;
118 if (error == MPD_ERROR_SERVER)
119 error = error | (mpd_connection_get_server_error(c->connection) << 8);
121 for (GList *list = c->error_callbacks; list != NULL;
122 list = list->next) {
123 mpdc_error_cb_t cb = list->data;
124 cb(c, error, mpd_connection_get_error_message(c->connection));
125 }
127 if (!mpd_connection_clear_error(c->connection))
128 mpdclient_disconnect(c);
130 return error;
131 }
133 gint
134 mpdclient_finish_command(struct mpdclient *c)
135 {
136 return mpd_response_finish(c->connection)
137 ? 0 : mpdclient_handle_error(c);
138 }
140 struct mpdclient *
141 mpdclient_new(void)
142 {
143 struct mpdclient *c;
145 c = g_new0(struct mpdclient, 1);
146 playlist_init(&c->playlist);
147 c->volume = MPD_STATUS_NO_VOLUME;
149 return c;
150 }
152 void
153 mpdclient_free(struct mpdclient *c)
154 {
155 mpdclient_disconnect(c);
157 mpdclient_playlist_free(&c->playlist);
159 g_list_free(c->error_callbacks);
160 g_list_free(c->playlist_callbacks);
161 g_list_free(c->browse_callbacks);
162 g_free(c);
163 }
165 gint
166 mpdclient_disconnect(struct mpdclient *c)
167 {
168 if (c->connection)
169 mpd_connection_free(c->connection);
170 c->connection = NULL;
172 if (c->status)
173 mpd_status_free(c->status);
174 c->status = NULL;
176 playlist_clear(&c->playlist);
178 if (c->song)
179 c->song = NULL;
181 return 0;
182 }
184 gint
185 mpdclient_connect(struct mpdclient *c,
186 const gchar *host,
187 gint port,
188 gfloat _timeout,
189 const gchar *password)
190 {
191 gint retval = 0;
193 /* close any open connection */
194 if( c->connection )
195 mpdclient_disconnect(c);
197 /* connect to MPD */
198 c->connection = mpd_connection_new(host, port, _timeout * 1000);
199 if (c->connection == NULL)
200 g_error("Out of memory");
202 if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
203 retval = mpdclient_handle_error(c);
204 if (retval != 0) {
205 mpd_connection_free(c->connection);
206 c->connection = NULL;
207 }
209 return retval;
210 }
212 /* send password */
213 if( password ) {
214 mpd_send_password(c->connection, password);
215 retval = mpdclient_finish_command(c);
216 }
218 return retval;
219 }
221 gint
222 mpdclient_update(struct mpdclient *c)
223 {
224 gint retval = 0;
226 c->volume = MPD_STATUS_NO_VOLUME;
228 if (MPD_ERROR(c))
229 return -1;
231 /* free the old status */
232 if (c->status)
233 mpd_status_free(c->status);
235 /* retrieve new status */
236 c->status = mpd_run_status(c->connection);
237 if (c->status == NULL)
238 return mpdclient_handle_error(c);
240 if (c->updatingdb &&
241 c->updatingdb != mpd_status_get_update_id(c->status))
242 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
244 c->updatingdb = mpd_status_get_update_id(c->status);
245 c->volume = mpd_status_get_volume(c->status);
247 /* check if the playlist needs an update */
248 if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
249 if (playlist_is_empty(&c->playlist))
250 retval = mpdclient_playlist_update_changes(c);
251 else
252 retval = mpdclient_playlist_update(c);
253 }
255 /* update the current song */
256 if (!c->song || mpd_status_get_song_id(c->status)) {
257 c->song = playlist_get_song(c, mpd_status_get_song_pos(c->status));
258 }
260 return retval;
261 }
264 /****************************************************************************/
265 /*** MPD Commands **********************************************************/
266 /****************************************************************************/
268 gint
269 mpdclient_cmd_play(struct mpdclient *c, gint idx)
270 {
271 struct mpd_song *song = playlist_get_song(c, idx);
273 if (MPD_ERROR(c))
274 return -1;
276 if (song)
277 mpd_send_play_id(c->connection, mpd_song_get_id(song));
278 else
279 mpd_send_play(c->connection);
281 return mpdclient_finish_command(c);
282 }
284 gint
285 mpdclient_cmd_pause(struct mpdclient *c, gint value)
286 {
287 if (MPD_ERROR(c))
288 return -1;
290 mpd_send_pause(c->connection, value);
291 return mpdclient_finish_command(c);
292 }
294 gint
295 mpdclient_cmd_crop(struct mpdclient *c)
296 {
297 struct mpd_status *status;
298 bool playing;
299 int length, current;
301 if (MPD_ERROR(c))
302 return -1;
304 status = mpd_run_status(c->connection);
305 if (status == NULL)
306 return mpdclient_handle_error(c);
308 playing = mpd_status_get_state(status) == MPD_STATE_PLAY ||
309 mpd_status_get_state(status) == MPD_STATE_PAUSE;
310 length = mpd_status_get_queue_length(status);
311 current = mpd_status_get_song_pos(status);
313 mpd_status_free(status);
315 if (!playing || length < 2)
316 return 0;
318 mpd_command_list_begin(c->connection, false);
320 while (--length >= 0)
321 if (length != current)
322 mpd_send_delete(c->connection, length);
324 mpd_command_list_end(c->connection);
326 return mpdclient_finish_command(c);
327 }
329 gint
330 mpdclient_cmd_stop(struct mpdclient *c)
331 {
332 if (MPD_ERROR(c))
333 return -1;
335 mpd_send_stop(c->connection);
336 return mpdclient_finish_command(c);
337 }
339 gint
340 mpdclient_cmd_next(struct mpdclient *c)
341 {
342 if (MPD_ERROR(c))
343 return -1;
345 mpd_send_next(c->connection);
346 return mpdclient_finish_command(c);
347 }
349 gint
350 mpdclient_cmd_prev(struct mpdclient *c)
351 {
352 if (MPD_ERROR(c))
353 return -1;
355 mpd_send_previous(c->connection);
356 return mpdclient_finish_command(c);
357 }
359 gint
360 mpdclient_cmd_seek(struct mpdclient *c, gint id, gint pos)
361 {
362 if (MPD_ERROR(c))
363 return -1;
365 mpd_send_seek_id(c->connection, id, pos);
366 return mpdclient_finish_command(c);
367 }
369 gint
370 mpdclient_cmd_shuffle(struct mpdclient *c)
371 {
372 if (MPD_ERROR(c))
373 return -1;
375 mpd_send_shuffle(c->connection);
376 return mpdclient_finish_command(c);
377 }
379 gint
380 mpdclient_cmd_shuffle_range(struct mpdclient *c, guint start, guint end)
381 {
382 mpd_send_shuffle_range(c->connection, start, end);
383 return mpdclient_finish_command(c);
384 }
386 gint
387 mpdclient_cmd_clear(struct mpdclient *c)
388 {
389 gint retval = 0;
391 if (MPD_ERROR(c))
392 return -1;
394 mpd_send_clear(c->connection);
395 retval = mpdclient_finish_command(c);
396 /* call playlist updated callback */
397 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
398 return retval;
399 }
401 gint
402 mpdclient_cmd_repeat(struct mpdclient *c, gint value)
403 {
404 if (MPD_ERROR(c))
405 return -1;
407 mpd_send_repeat(c->connection, value);
408 return mpdclient_finish_command(c);
409 }
411 gint
412 mpdclient_cmd_random(struct mpdclient *c, gint value)
413 {
414 if (MPD_ERROR(c))
415 return -1;
417 mpd_send_random(c->connection, value);
418 return mpdclient_finish_command(c);
419 }
421 gint
422 mpdclient_cmd_single(struct mpdclient *c, gint value)
423 {
424 if (MPD_ERROR(c))
425 return -1;
427 mpd_send_single(c->connection, value);
428 return mpdclient_finish_command(c);
429 }
431 gint
432 mpdclient_cmd_consume(struct mpdclient *c, gint value)
433 {
434 if (MPD_ERROR(c))
435 return -1;
437 mpd_send_consume(c->connection, value);
438 return mpdclient_finish_command(c);
439 }
441 gint
442 mpdclient_cmd_crossfade(struct mpdclient *c, gint value)
443 {
444 if (MPD_ERROR(c))
445 return -1;
447 mpd_send_crossfade(c->connection, value);
448 return mpdclient_finish_command(c);
449 }
451 gint
452 mpdclient_cmd_db_update(struct mpdclient *c, const gchar *path)
453 {
454 gint ret;
456 if (MPD_ERROR(c))
457 return -1;
459 mpd_send_update(c->connection, path ? path : "");
460 ret = mpdclient_finish_command(c);
462 if (ret == 0)
463 /* set updatingDb to make sure the browse callback
464 gets called even if the update has finished before
465 status is updated */
466 c->updatingdb = 1;
468 return ret;
469 }
471 gint
472 mpdclient_cmd_volume(struct mpdclient *c, gint value)
473 {
474 if (MPD_ERROR(c))
475 return -1;
477 mpd_send_set_volume(c->connection, value);
478 return mpdclient_finish_command(c);
479 }
481 gint mpdclient_cmd_volume_up(struct mpdclient *c)
482 {
483 if (MPD_ERROR(c))
484 return -1;
486 if (c->status == NULL ||
487 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
488 return 0;
490 if (c->volume == MPD_STATUS_NO_VOLUME)
491 c->volume = mpd_status_get_volume(c->status);
493 if (c->volume >= 100)
494 return 0;
496 return mpdclient_cmd_volume(c, ++c->volume);
497 }
499 gint mpdclient_cmd_volume_down(struct mpdclient *c)
500 {
501 if (MPD_ERROR(c))
502 return -1;
504 if (c->status == NULL ||
505 mpd_status_get_volume(c->status) == MPD_STATUS_NO_VOLUME)
506 return 0;
508 if (c->volume == MPD_STATUS_NO_VOLUME)
509 c->volume = mpd_status_get_volume(c->status);
511 if (c->volume <= 0)
512 return 0;
514 return mpdclient_cmd_volume(c, --c->volume);
515 }
517 gint
518 mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8)
519 {
520 if (MPD_ERROR(c))
521 return -1;
523 mpd_send_add(c->connection, path_utf8);
524 return mpdclient_finish_command(c);
525 }
527 gint
528 mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
529 {
530 gint retval = 0;
532 if (MPD_ERROR(c))
533 return -1;
535 if (song == NULL)
536 return -1;
538 /* send the add command to mpd */
539 mpd_send_add(c->connection, mpd_song_get_uri(song));
540 if( (retval=mpdclient_finish_command(c)) )
541 return retval;
543 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
544 /* add the song to playlist */
545 playlist_append(&c->playlist, song);
547 /* increment the playlist id, so we don't retrieve a new playlist */
548 c->playlist.id++;
550 /* call playlist updated callback */
551 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
552 #endif
554 return 0;
555 }
557 gint
558 mpdclient_cmd_delete(struct mpdclient *c, gint idx)
559 {
560 gint retval = 0;
561 struct mpd_song *song;
563 if (MPD_ERROR(c))
564 return -1;
566 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
567 return -1;
569 song = playlist_get(&c->playlist, idx);
571 /* send the delete command to mpd */
572 mpd_send_delete_id(c->connection, mpd_song_get_id(song));
573 if( (retval=mpdclient_finish_command(c)) )
574 return retval;
576 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
577 /* increment the playlist id, so we don't retrieve a new playlist */
578 c->playlist.id++;
580 /* remove the song from the playlist */
581 playlist_remove_reuse(&c->playlist, idx);
583 /* call playlist updated callback */
584 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
586 /* remove references to the song */
587 if (c->song == song)
588 c->song = NULL;
590 mpd_song_free(song);
591 #endif
593 return 0;
594 }
596 gint
597 mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index)
598 {
599 gint n;
600 struct mpd_song *song1, *song2;
602 if (MPD_ERROR(c))
603 return -1;
605 if (old_index == new_index || new_index < 0 ||
606 (guint)new_index >= c->playlist.list->len)
607 return -1;
609 song1 = playlist_get(&c->playlist, old_index);
610 song2 = playlist_get(&c->playlist, new_index);
612 /* send the move command to mpd */
613 mpd_send_swap_id(c->connection,
614 mpd_song_get_id(song1), mpd_song_get_id(song2));
615 if( (n=mpdclient_finish_command(c)) )
616 return n;
618 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
619 /* update the playlist */
620 playlist_swap(&c->playlist, old_index, new_index);
622 /* increment the playlist id, so we don't retrieve a new playlist */
623 c->playlist.id++;
624 #endif
626 /* call playlist updated callback */
627 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
629 return 0;
630 }
632 gint
633 mpdclient_cmd_save_playlist(struct mpdclient *c, const gchar *filename_utf8)
634 {
635 gint retval = 0;
637 if (MPD_ERROR(c))
638 return -1;
640 mpd_send_save(c->connection, filename_utf8);
641 if ((retval = mpdclient_finish_command(c)) == 0)
642 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
643 return retval;
644 }
646 gint
647 mpdclient_cmd_load_playlist(struct mpdclient *c, const gchar *filename_utf8)
648 {
649 if (MPD_ERROR(c))
650 return -1;
652 mpd_send_load(c->connection, filename_utf8);
653 return mpdclient_finish_command(c);
654 }
656 gint
657 mpdclient_cmd_delete_playlist(struct mpdclient *c, const gchar *filename_utf8)
658 {
659 gint retval = 0;
661 if (MPD_ERROR(c))
662 return -1;
664 mpd_send_rm(c->connection, filename_utf8);
665 if ((retval = mpdclient_finish_command(c)) == 0)
666 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
667 return retval;
668 }
671 /****************************************************************************/
672 /*** Callback management functions ******************************************/
673 /****************************************************************************/
675 static void
676 do_list_callbacks(struct mpdclient *c, GList *list, gint event, gpointer data)
677 {
678 while (list) {
679 mpdc_list_cb_t fn = list->data;
681 fn(c, event, data);
682 list = list->next;
683 }
684 }
686 void
687 mpdclient_playlist_callback(struct mpdclient *c, int event, gpointer data)
688 {
689 do_list_callbacks(c, c->playlist_callbacks, event, data);
690 }
692 void
693 mpdclient_install_playlist_callback(struct mpdclient *c,mpdc_list_cb_t cb)
694 {
695 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
696 }
698 void
699 mpdclient_remove_playlist_callback(struct mpdclient *c, mpdc_list_cb_t cb)
700 {
701 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
702 }
704 void
705 mpdclient_browse_callback(struct mpdclient *c, int event, gpointer data)
706 {
707 do_list_callbacks(c, c->browse_callbacks, event, data);
708 }
711 void
712 mpdclient_install_browse_callback(struct mpdclient *c,mpdc_list_cb_t cb)
713 {
714 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
715 }
717 void
718 mpdclient_remove_browse_callback(struct mpdclient *c, mpdc_list_cb_t cb)
719 {
720 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
721 }
723 void
724 mpdclient_install_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
725 {
726 c->error_callbacks = g_list_append(c->error_callbacks, cb);
727 }
729 void
730 mpdclient_remove_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
731 {
732 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
733 }
736 /****************************************************************************/
737 /*** Playlist management functions ******************************************/
738 /****************************************************************************/
740 /* update playlist */
741 gint
742 mpdclient_playlist_update(struct mpdclient *c)
743 {
744 struct mpd_entity *entity;
746 if (MPD_ERROR(c))
747 return -1;
749 playlist_clear(&c->playlist);
751 mpd_send_list_queue_meta(c->connection);
752 while ((entity = mpd_recv_entity(c->connection))) {
753 if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG)
754 playlist_append(&c->playlist, mpd_entity_get_song(entity));
756 mpd_entity_free(entity);
757 }
759 c->playlist.id = mpd_status_get_queue_version(c->status);
760 c->song = NULL;
762 /* call playlist updated callbacks */
763 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
765 return mpdclient_finish_command(c);
766 }
768 /* update playlist (plchanges) */
769 gint
770 mpdclient_playlist_update_changes(struct mpdclient *c)
771 {
772 struct mpd_song *song;
773 guint length;
775 if (MPD_ERROR(c))
776 return -1;
778 mpd_send_queue_changes_meta(c->connection, c->playlist.id);
780 while ((song = mpd_recv_song(c->connection)) != NULL) {
781 int pos = mpd_song_get_pos(song);
783 if (pos >= 0 && (guint)pos < c->playlist.list->len) {
784 /* update song */
785 playlist_replace(&c->playlist, pos, song);
786 } else {
787 /* add a new song */
788 playlist_append(&c->playlist, song);
789 }
791 mpd_song_free(song);
792 }
794 /* remove trailing songs */
796 length = mpd_status_get_queue_length(c->status);
797 while (length < c->playlist.list->len) {
798 guint pos = c->playlist.list->len - 1;
800 /* Remove the last playlist entry */
801 playlist_remove(&c->playlist, pos);
802 }
804 c->song = NULL;
805 c->playlist.id = mpd_status_get_queue_version(c->status);
807 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
809 return 0;
810 }
813 /****************************************************************************/
814 /*** Filelist functions *****************************************************/
815 /****************************************************************************/
817 struct filelist *
818 mpdclient_filelist_get(struct mpdclient *c, const gchar *path)
819 {
820 struct filelist *filelist;
821 struct mpd_entity *entity;
823 if (MPD_ERROR(c))
824 return NULL;
826 mpd_send_list_meta(c->connection, path);
827 filelist = filelist_new();
828 if (path && path[0] && strcmp(path, "/"))
829 /* add a dummy entry for ./.. */
830 filelist_append(filelist, NULL);
832 while ((entity = mpd_recv_entity(c->connection)) != NULL)
833 filelist_append(filelist, entity);
835 /* If there's an error, ignore it. We'll return an empty filelist. */
836 mpdclient_finish_command(c);
838 filelist_sort_dir_play(filelist, compare_filelistentry);
840 return filelist;
841 }
843 static struct filelist *
844 mpdclient_recv_filelist_response(struct mpdclient *c)
845 {
846 struct filelist *filelist;
847 struct mpd_entity *entity;
849 filelist = filelist_new();
851 while ((entity = mpd_recv_entity(c->connection)) != NULL)
852 filelist_append(filelist, entity);
854 if (mpdclient_finish_command(c)) {
855 filelist_free(filelist);
856 return NULL;
857 }
859 return filelist;
860 }
862 struct filelist *
863 mpdclient_filelist_search(struct mpdclient *c,
864 int exact_match,
865 enum mpd_tag_type tag,
866 gchar *filter_utf8)
867 {
868 if (MPD_ERROR(c))
869 return NULL;
871 mpd_search_db_songs(c->connection, exact_match);
872 mpd_search_add_tag_constraint(c->connection, MPD_OPERATOR_DEFAULT,
873 tag, filter_utf8);
874 mpd_search_commit(c->connection);
876 return mpdclient_recv_filelist_response(c);
877 }
879 int
880 mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl)
881 {
882 guint i;
884 if (MPD_ERROR(c))
885 return -1;
887 if (filelist_is_empty(fl))
888 return 0;
890 mpd_command_list_begin(c->connection, false);
892 for (i = 0; i < filelist_length(fl); ++i) {
893 struct filelist_entry *entry = filelist_get(fl, i);
894 struct mpd_entity *entity = entry->entity;
896 if (entity != NULL &&
897 mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
898 const struct mpd_song *song =
899 mpd_entity_get_song(entity);
900 const char *uri = mpd_song_get_uri(song);
902 if (uri != NULL)
903 mpd_send_add(c->connection, uri);
904 }
905 }
907 mpd_command_list_end(c->connection);
908 return mpdclient_finish_command(c);
909 }
911 GList *
912 mpdclient_get_artists(struct mpdclient *c)
913 {
914 GList *list = NULL;
915 struct mpd_pair *pair;
917 if (MPD_ERROR(c))
918 return NULL;
920 mpd_search_db_tags(c->connection, MPD_TAG_ARTIST);
921 mpd_search_commit(c->connection);
923 while ((pair = mpd_recv_pair_tag(c->connection,
924 MPD_TAG_ARTIST)) != NULL) {
925 list = g_list_append(list, g_strdup(pair->value));
926 mpd_return_pair(c->connection, pair);
927 }
929 if (mpdclient_finish_command(c))
930 return string_list_free(list);
932 return list;
933 }
935 GList *
936 mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8)
937 {
938 GList *list = NULL;
939 struct mpd_pair *pair;
941 if (MPD_ERROR(c))
942 return NULL;
944 mpd_search_db_tags(c->connection, MPD_TAG_ALBUM);
945 if (artist_utf8 != NULL)
946 mpd_search_add_tag_constraint(c->connection,
947 MPD_OPERATOR_DEFAULT,
948 MPD_TAG_ARTIST, artist_utf8);
949 mpd_search_commit(c->connection);
951 while ((pair = mpd_recv_pair_tag(c->connection,
952 MPD_TAG_ALBUM)) != NULL) {
953 list = g_list_append(list, g_strdup(pair->value));
954 mpd_return_pair(c->connection, pair);
955 }
957 if (mpdclient_finish_command(c))
958 return string_list_free(list);
960 return list;
961 }