1 /*
2 * (c) 2004 by Kalle Wallin <kaw@linux.se>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 */
19 #include "mpdclient.h"
20 #include "screen_utils.h"
21 #include "config.h"
22 #include "ncmpc.h"
23 #include "support.h"
24 #include "options.h"
25 #include "strfsong.h"
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <time.h>
30 #include <string.h>
32 #undef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
33 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
34 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
35 #define ENABLE_SONG_ID
36 #define ENABLE_PLCHANGES
38 #define BUFSIZE 1024
40 #define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)
42 /* from utils.c */
43 extern GList *string_list_free(GList *string_list);
46 /* filelist sorting functions */
47 static gint
48 compare_filelistentry_dir(gconstpointer filelist_entry1,
49 gconstpointer filelist_entry2)
50 {
51 const mpd_InfoEntity *e1, *e2;
52 char *key1, *key2;
53 int n = 0;
55 e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
56 e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
58 if (e1 && e2 &&
59 e1->type == MPD_INFO_ENTITY_TYPE_DIRECTORY &&
60 e2->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
61 key1 = g_utf8_collate_key(e1->info.directory->path,-1);
62 key2 = g_utf8_collate_key(e2->info.directory->path,-1);
63 n = strcmp(key1,key2);
64 g_free(key1);
65 g_free(key2);
66 }
68 return n;
69 }
71 /* sort by list-format */
72 gint
73 compare_filelistentry_format(gconstpointer filelist_entry1,
74 gconstpointer filelist_entry2)
75 {
76 const mpd_InfoEntity *e1, *e2;
77 char key1[BUFSIZE], key2[BUFSIZE];
78 int n = 0;
80 e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
81 e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
83 if (e1 && e2 &&
84 e1->type == MPD_INFO_ENTITY_TYPE_SONG &&
85 e2->type == MPD_INFO_ENTITY_TYPE_SONG) {
86 strfsong(key1, BUFSIZE, LIST_FORMAT, e1->info.song);
87 strfsong(key2, BUFSIZE, LIST_FORMAT, e2->info.song);
88 n = strcmp(key1,key2);
89 }
91 return n;
92 }
95 /* Error callbacks */
96 static gint
97 error_cb(mpdclient_t *c, gint error, gchar *msg)
98 {
99 GList *list = c->error_callbacks;
101 if (list == NULL)
102 fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
104 while (list) {
105 mpdc_error_cb_t cb = list->data;
106 if (cb)
107 cb(c, error, msg);
108 list = list->next;
109 }
111 mpd_clearError(c->connection);
112 return error;
113 }
116 /****************************************************************************/
117 /*** mpdclient functions ****************************************************/
118 /****************************************************************************/
120 gint
121 mpdclient_finish_command(mpdclient_t *c)
122 {
123 mpd_finishCommand(c->connection);
125 if (c->connection->error) {
126 gint error = c->connection->error;
127 gchar *msg;
129 if (error == MPD_ERROR_ACK &&
130 c->connection->errorCode == MPD_ACK_ERROR_PERMISSION &&
131 screen_auth(c) == 0)
132 return 0;
134 if (error == MPD_ERROR_ACK)
135 error = error | (c->connection->errorCode << 8);
137 msg = locale_to_utf8(c->connection->errorStr);
138 error_cb(c, error, msg);
139 g_free(msg);
140 return error;
141 }
143 return 0;
144 }
146 mpdclient_t *
147 mpdclient_new(void)
148 {
149 mpdclient_t *c;
151 c = g_malloc0(sizeof(mpdclient_t));
152 playlist_init(&c->playlist);
154 return c;
155 }
157 void
158 mpdclient_free(mpdclient_t *c)
159 {
160 mpdclient_disconnect(c);
162 mpdclient_playlist_free(&c->playlist);
164 g_list_free(c->error_callbacks);
165 g_list_free(c->playlist_callbacks);
166 g_list_free(c->browse_callbacks);
167 g_free(c);
168 }
170 gint
171 mpdclient_disconnect(mpdclient_t *c)
172 {
173 if (c->connection)
174 mpd_closeConnection(c->connection);
175 c->connection = NULL;
177 if (c->status)
178 mpd_freeStatus(c->status);
179 c->status = NULL;
181 playlist_clear(&c->playlist);
183 if (c->song)
184 c->song = NULL;
186 return 0;
187 }
189 gint
190 mpdclient_connect(mpdclient_t *c,
191 gchar *host,
192 gint port,
193 gfloat _timeout,
194 gchar *password)
195 {
196 gint retval = 0;
198 /* close any open connection */
199 if( c->connection )
200 mpdclient_disconnect(c);
202 /* connect to MPD */
203 c->connection = mpd_newConnection(host, port, _timeout);
204 if( c->connection->error )
205 return error_cb(c, c->connection->error,
206 c->connection->errorStr);
208 /* send password */
209 if( password ) {
210 mpd_sendPasswordCommand(c->connection, password);
211 retval = mpdclient_finish_command(c);
212 }
213 c->need_update = TRUE;
215 return retval;
216 }
218 gint
219 mpdclient_update(mpdclient_t *c)
220 {
221 gint retval = 0;
223 if (MPD_ERROR(c))
224 return -1;
226 /* free the old status */
227 if (c->status)
228 mpd_freeStatus(c->status);
230 /* retreive new status */
231 mpd_sendStatusCommand(c->connection);
232 c->status = mpd_getStatus(c->connection);
233 if ((retval=mpdclient_finish_command(c)))
234 return retval;
236 /* check if the playlist needs an update */
237 if (c->playlist.id != c->status->playlist) {
238 if (playlist_is_empty(&c->playlist))
239 retval = mpdclient_playlist_update_changes(c);
240 else
241 retval = mpdclient_playlist_update(c);
242 }
244 /* update the current song */
245 if (!c->song || c->status->songid != c->song->id) {
246 c->song = playlist_get_song(c, c->status->song);
247 }
249 c->need_update = FALSE;
251 return retval;
252 }
255 /****************************************************************************/
256 /*** MPD Commands **********************************************************/
257 /****************************************************************************/
259 gint
260 mpdclient_cmd_play(mpdclient_t *c, gint idx)
261 {
262 #ifdef ENABLE_SONG_ID
263 struct mpd_song *song = playlist_get_song(c, idx);
265 if (song)
266 mpd_sendPlayIdCommand(c->connection, song->id);
267 else
268 mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
269 #else
270 mpd_sendPlayCommand(c->connection, idx);
271 #endif
272 c->need_update = TRUE;
273 return mpdclient_finish_command(c);
274 }
276 gint
277 mpdclient_cmd_pause(mpdclient_t *c, gint value)
278 {
279 mpd_sendPauseCommand(c->connection, value);
280 return mpdclient_finish_command(c);
281 }
283 gint
284 mpdclient_cmd_crop(mpdclient_t *c)
285 {
286 mpd_Status *status;
287 int length;
289 mpd_sendStatusCommand(c->connection);
290 status = mpd_getStatus(c->connection);
291 length = status->playlistLength - 1;
293 if (length <= 0) {
294 mpd_freeStatus(status);
295 } else if (status->state == 3 || status->state == 2) {
296 /* If playing or paused */
298 mpd_sendCommandListBegin( c->connection );
300 while (length >= 0) {
301 if (length != status->song)
302 mpd_sendDeleteCommand(c->connection, length);
304 length--;
305 }
307 mpd_sendCommandListEnd(c->connection);
308 mpd_freeStatus(status);
309 } else {
310 mpd_freeStatus(status);
311 }
313 return mpdclient_finish_command(c);
314 }
316 gint
317 mpdclient_cmd_stop(mpdclient_t *c)
318 {
319 mpd_sendStopCommand(c->connection);
320 return mpdclient_finish_command(c);
321 }
323 gint
324 mpdclient_cmd_next(mpdclient_t *c)
325 {
326 mpd_sendNextCommand(c->connection);
327 c->need_update = TRUE;
328 return mpdclient_finish_command(c);
329 }
331 gint
332 mpdclient_cmd_prev(mpdclient_t *c)
333 {
334 mpd_sendPrevCommand(c->connection);
335 c->need_update = TRUE;
336 return mpdclient_finish_command(c);
337 }
339 gint
340 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
341 {
342 mpd_sendSeekIdCommand(c->connection, id, pos);
343 return mpdclient_finish_command(c);
344 }
346 gint
347 mpdclient_cmd_shuffle(mpdclient_t *c)
348 {
349 mpd_sendShuffleCommand(c->connection);
350 c->need_update = TRUE;
351 return mpdclient_finish_command(c);
352 }
354 gint
355 mpdclient_cmd_clear(mpdclient_t *c)
356 {
357 gint retval = 0;
359 mpd_sendClearCommand(c->connection);
360 retval = mpdclient_finish_command(c);
361 /* call playlist updated callback */
362 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
363 c->need_update = TRUE;
364 return retval;
365 }
367 gint
368 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
369 {
370 mpd_sendRepeatCommand(c->connection, value);
371 return mpdclient_finish_command(c);
372 }
374 gint
375 mpdclient_cmd_random(mpdclient_t *c, gint value)
376 {
377 mpd_sendRandomCommand(c->connection, value);
378 return mpdclient_finish_command(c);
379 }
381 gint
382 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
383 {
384 mpd_sendCrossfadeCommand(c->connection, value);
385 return mpdclient_finish_command(c);
386 }
388 gint
389 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
390 {
391 mpd_sendUpdateCommand(c->connection, path ? path : "");
392 return mpdclient_finish_command(c);
393 }
395 gint
396 mpdclient_cmd_volume(mpdclient_t *c, gint value)
397 {
398 mpd_sendSetvolCommand(c->connection, value);
399 return mpdclient_finish_command(c);
400 }
402 gint
403 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
404 {
405 mpd_sendAddCommand(c->connection, path_utf8);
406 return mpdclient_finish_command(c);
407 }
409 gint
410 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
411 {
412 gint retval;
413 gchar *path_utf8 = locale_to_utf8(path);
415 retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
416 g_free(path_utf8);
417 return retval;
418 }
420 gint
421 mpdclient_cmd_add(mpdclient_t *c, struct mpd_song *song)
422 {
423 gint retval = 0;
425 if( !song || !song->file )
426 return -1;
428 /* send the add command to mpd */
429 mpd_sendAddCommand(c->connection, song->file);
430 if( (retval=mpdclient_finish_command(c)) )
431 return retval;
433 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
434 /* add the song to playlist */
435 playlist_append(&c->playlist, song);
437 /* increment the playlist id, so we dont retrives a new playlist */
438 c->playlist.id++;
440 /* call playlist updated callback */
441 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
442 #else
443 c->need_update = TRUE;
444 #endif
446 return 0;
447 }
449 gint
450 mpdclient_cmd_delete(mpdclient_t *c, gint idx)
451 {
452 gint retval = 0;
453 struct mpd_song *song;
455 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
456 return -1;
458 song = playlist_get(&c->playlist, idx);
460 /* send the delete command to mpd */
461 #ifdef ENABLE_SONG_ID
462 mpd_sendDeleteIdCommand(c->connection, song->id);
463 #else
464 mpd_sendDeleteCommand(c->connection, idx);
465 #endif
466 if( (retval=mpdclient_finish_command(c)) )
467 return retval;
469 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
470 /* increment the playlist id, so we dont retrive a new playlist */
471 c->playlist.id++;
473 /* remove the song from the playlist */
474 playlist_remove_reuse(&c->playlist, idx);
476 /* call playlist updated callback */
477 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
479 /* remove references to the song */
480 if (c->song == song) {
481 c->song = NULL;
482 c->need_update = TRUE;
483 }
485 mpd_freeSong(song);
487 #else
488 c->need_update = TRUE;
489 #endif
491 return 0;
492 }
494 gint
495 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
496 {
497 gint n;
498 struct mpd_song *song1, *song2;
500 if (old_index == new_index || new_index < 0 ||
501 (guint)new_index >= c->playlist.list->len)
502 return -1;
504 song1 = playlist_get(&c->playlist, old_index);
505 song2 = playlist_get(&c->playlist, new_index);
507 /* send the move command to mpd */
508 #ifdef ENABLE_SONG_ID
509 mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
510 #else
511 mpd_sendMoveCommand(c->connection, old_index, new_index);
512 #endif
513 if( (n=mpdclient_finish_command(c)) )
514 return n;
516 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
517 /* update the playlist */
518 playlist_swap(&c->playlist, old_index, new_index);
520 /* increment the playlist id, so we dont retrives a new playlist */
521 c->playlist.id++;
523 #else
524 c->need_update = TRUE;
525 #endif
527 /* call playlist updated callback */
528 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
530 return 0;
531 }
533 gint
534 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
535 {
536 gint retval = 0;
538 mpd_sendSaveCommand(c->connection, filename_utf8);
539 if ((retval = mpdclient_finish_command(c)) == 0)
540 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
541 return retval;
542 }
544 gint
545 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
546 {
547 gint retval = 0;
548 gchar *filename_utf8 = locale_to_utf8(filename);
550 retval = mpdclient_cmd_save_playlist_utf8(c, filename);
551 g_free(filename_utf8);
552 return retval;
553 }
555 gint
556 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
557 {
558 mpd_sendLoadCommand(c->connection, filename_utf8);
559 c->need_update = TRUE;
560 return mpdclient_finish_command(c);
561 }
563 gint
564 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
565 {
566 gint retval = 0;
568 mpd_sendRmCommand(c->connection, filename_utf8);
569 if ((retval = mpdclient_finish_command(c)) == 0)
570 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
571 return retval;
572 }
574 gint
575 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
576 {
577 gint retval = 0;
578 gchar *filename_utf8 = locale_to_utf8(filename);
580 retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
581 g_free(filename_utf8);
582 return retval;
583 }
586 /****************************************************************************/
587 /*** Callback managment functions *******************************************/
588 /****************************************************************************/
590 static void
591 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
592 {
593 while (list) {
594 mpdc_list_cb_t fn = list->data;
596 fn(c, event, data);
597 list = list->next;
598 }
599 }
601 void
602 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
603 {
604 do_list_callbacks(c, c->playlist_callbacks, event, data);
605 }
607 void
608 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
609 {
610 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
611 }
613 void
614 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
615 {
616 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
617 }
619 void
620 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
621 {
622 do_list_callbacks(c, c->browse_callbacks, event, data);
623 }
626 void
627 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
628 {
629 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
630 }
632 void
633 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
634 {
635 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
636 }
638 void
639 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
640 {
641 c->error_callbacks = g_list_append(c->error_callbacks, cb);
642 }
644 void
645 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
646 {
647 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
648 }
651 /****************************************************************************/
652 /*** Playlist managment functions *******************************************/
653 /****************************************************************************/
655 /* update playlist */
656 gint
657 mpdclient_playlist_update(mpdclient_t *c)
658 {
659 mpd_InfoEntity *entity;
661 if (MPD_ERROR(c))
662 return -1;
664 playlist_clear(&c->playlist);
666 mpd_sendPlaylistInfoCommand(c->connection,-1);
667 while ((entity = mpd_getNextInfoEntity(c->connection))) {
668 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
669 playlist_append(&c->playlist, entity->info.song);
671 mpd_freeInfoEntity(entity);
672 }
674 c->playlist.id = c->status->playlist;
675 c->song = NULL;
677 /* call playlist updated callbacks */
678 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
680 return mpdclient_finish_command(c);
681 }
683 #ifdef ENABLE_PLCHANGES
685 /* update playlist (plchanges) */
686 gint
687 mpdclient_playlist_update_changes(mpdclient_t *c)
688 {
689 mpd_InfoEntity *entity;
691 if (MPD_ERROR(c))
692 return -1;
694 mpd_sendPlChangesCommand(c->connection, c->playlist.id);
696 while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) {
697 struct mpd_song *song = entity->info.song;
699 if (song->pos >= 0 && (guint)song->pos < c->playlist.list->len) {
700 /* update song */
701 playlist_replace(&c->playlist, song->pos, song);
702 } else {
703 /* add a new song */
704 playlist_append(&c->playlist, song);
705 }
707 mpd_freeInfoEntity(entity);
708 }
710 /* remove trailing songs */
711 while ((guint)c->status->playlistLength < c->playlist.list->len) {
712 guint pos = c->playlist.list->len - 1;
714 /* Remove the last playlist entry */
715 playlist_remove(&c->playlist, pos);
716 }
718 c->song = NULL;
719 c->playlist.id = c->status->playlist;
721 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
723 return 0;
724 }
726 #else
727 gint
728 mpdclient_playlist_update_changes(mpdclient_t *c)
729 {
730 return mpdclient_playlist_update(c);
731 }
732 #endif
735 /****************************************************************************/
736 /*** Filelist functions *****************************************************/
737 /****************************************************************************/
739 mpdclient_filelist_t *
740 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
741 {
742 mpdclient_filelist_t *filelist;
743 mpd_InfoEntity *entity;
744 gchar *path_utf8 = locale_to_utf8(path);
745 gboolean has_dirs_only = TRUE;
747 mpd_sendLsInfoCommand(c->connection, path_utf8);
748 filelist = filelist_new(path);
749 if (path && path[0] && strcmp(path, "/"))
750 /* add a dummy entry for ./.. */
751 filelist_append(filelist, NULL);
753 while ((entity=mpd_getNextInfoEntity(c->connection))) {
754 filelist_append(filelist, entity);
756 if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY) {
757 has_dirs_only = FALSE;
758 }
759 }
761 /* If there's an error, ignore it. We'll return an empty filelist. */
762 mpdclient_finish_command(c);
764 g_free(path_utf8);
766 // If there are only directory entities in the filelist, we sort it
767 if (has_dirs_only)
768 filelist_sort(filelist, compare_filelistentry_dir);
770 return filelist;
771 }
773 mpdclient_filelist_t *
774 mpdclient_filelist_search_utf8(mpdclient_t *c,
775 int exact_match,
776 int table,
777 gchar *filter_utf8)
778 {
779 mpdclient_filelist_t *filelist;
780 mpd_InfoEntity *entity;
782 if (exact_match)
783 mpd_sendFindCommand(c->connection, table, filter_utf8);
784 else
785 mpd_sendSearchCommand(c->connection, table, filter_utf8);
786 filelist = filelist_new(NULL);
788 while ((entity=mpd_getNextInfoEntity(c->connection)))
789 filelist_append(filelist, entity);
791 if (mpdclient_finish_command(c)) {
792 filelist_free(filelist);
793 return NULL;
794 }
796 return filelist;
797 }
800 mpdclient_filelist_t *
801 mpdclient_filelist_search(mpdclient_t *c,
802 int exact_match,
803 int table,
804 gchar *_filter)
805 {
806 mpdclient_filelist_t *filelist;
807 gchar *filter_utf8 = locale_to_utf8(_filter);
809 filelist = mpdclient_filelist_search_utf8(c, exact_match, table,
810 filter_utf8);
811 g_free(filter_utf8);
813 return filelist;
814 }
816 mpdclient_filelist_t *
817 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
818 {
819 if (filelist != NULL) {
820 gchar *path = g_strdup(filelist->path);
822 filelist_free(filelist);
823 filelist = mpdclient_filelist_get(c, path);
824 g_free(path);
825 return filelist;
826 }
827 return NULL;
828 }
830 int
831 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
832 {
833 guint i;
835 if (filelist_is_empty(fl))
836 return 0;
838 mpd_sendCommandListBegin(c->connection);
840 for (i = 0; i < filelist_length(fl); ++i) {
841 filelist_entry_t *entry = filelist_get(fl, i);
842 mpd_InfoEntity *entity = entry->entity;
844 if (entity && entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
845 struct mpd_song *song = entity->info.song;
847 mpd_sendAddCommand(c->connection, song->file);
848 }
849 }
851 mpd_sendCommandListEnd(c->connection);
852 return mpdclient_finish_command(c);
853 }
855 GList *
856 mpdclient_get_artists_utf8(mpdclient_t *c)
857 {
858 gchar *str = NULL;
859 GList *list = NULL;
861 mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
862 while ((str = mpd_getNextArtist(c->connection)))
863 list = g_list_append(list, (gpointer) str);
865 if (mpdclient_finish_command(c))
866 return string_list_free(list);
868 return list;
869 }
871 GList *
872 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
873 {
874 gchar *str = NULL;
875 GList *list = NULL;
877 mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
878 while ((str = mpd_getNextAlbum(c->connection)))
879 list = g_list_append(list, (gpointer) str);
881 if (mpdclient_finish_command(c))
882 return string_list_free(list);
884 return list;
885 }