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 "charset.h"
23 #include "options.h"
24 #include "strfsong.h"
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <time.h>
29 #include <string.h>
31 #undef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
32 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
33 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
34 #define ENABLE_SONG_ID
35 #define ENABLE_PLCHANGES
37 #define BUFSIZE 1024
39 #define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)
41 /* from utils.c */
42 extern GList *string_list_free(GList *string_list);
45 /* filelist sorting functions */
46 static gint
47 compare_filelistentry_dir(gconstpointer filelist_entry1,
48 gconstpointer filelist_entry2)
49 {
50 const mpd_InfoEntity *e1, *e2;
51 char *key1, *key2;
52 int n = 0;
54 e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
55 e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
57 if (e1 && e2 &&
58 e1->type == MPD_INFO_ENTITY_TYPE_DIRECTORY &&
59 e2->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
60 key1 = g_utf8_collate_key(e1->info.directory->path,-1);
61 key2 = g_utf8_collate_key(e2->info.directory->path,-1);
62 n = strcmp(key1,key2);
63 g_free(key1);
64 g_free(key2);
65 }
67 return n;
68 }
70 /* sort by list-format */
71 gint
72 compare_filelistentry_format(gconstpointer filelist_entry1,
73 gconstpointer filelist_entry2)
74 {
75 const mpd_InfoEntity *e1, *e2;
76 char key1[BUFSIZE], key2[BUFSIZE];
77 int n = 0;
79 e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
80 e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
82 if (e1 && e2 &&
83 e1->type == MPD_INFO_ENTITY_TYPE_SONG &&
84 e2->type == MPD_INFO_ENTITY_TYPE_SONG) {
85 strfsong(key1, BUFSIZE, options.list_format, e1->info.song);
86 strfsong(key2, BUFSIZE, options.list_format, e2->info.song);
87 n = strcmp(key1,key2);
88 }
90 return n;
91 }
94 /* Error callbacks */
95 static gint
96 error_cb(mpdclient_t *c, gint error, gchar *msg)
97 {
98 GList *list = c->error_callbacks;
100 if (list == NULL)
101 fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
103 while (list) {
104 mpdc_error_cb_t cb = list->data;
105 if (cb)
106 cb(c, error, msg);
107 list = list->next;
108 }
110 mpd_clearError(c->connection);
111 return error;
112 }
115 /****************************************************************************/
116 /*** mpdclient functions ****************************************************/
117 /****************************************************************************/
119 gint
120 mpdclient_finish_command(mpdclient_t *c)
121 {
122 mpd_finishCommand(c->connection);
124 if (c->connection->error) {
125 gint error = c->connection->error;
126 gchar *msg;
128 if (error == MPD_ERROR_ACK &&
129 c->connection->errorCode == MPD_ACK_ERROR_PERMISSION &&
130 screen_auth(c) == 0)
131 return 0;
133 if (error == MPD_ERROR_ACK)
134 error = error | (c->connection->errorCode << 8);
136 msg = locale_to_utf8(c->connection->errorStr);
137 error_cb(c, error, msg);
138 g_free(msg);
139 return error;
140 }
142 return 0;
143 }
145 mpdclient_t *
146 mpdclient_new(void)
147 {
148 mpdclient_t *c;
150 c = g_malloc0(sizeof(mpdclient_t));
151 playlist_init(&c->playlist);
153 return c;
154 }
156 void
157 mpdclient_free(mpdclient_t *c)
158 {
159 mpdclient_disconnect(c);
161 mpdclient_playlist_free(&c->playlist);
163 g_list_free(c->error_callbacks);
164 g_list_free(c->playlist_callbacks);
165 g_list_free(c->browse_callbacks);
166 g_free(c);
167 }
169 gint
170 mpdclient_disconnect(mpdclient_t *c)
171 {
172 if (c->connection)
173 mpd_closeConnection(c->connection);
174 c->connection = NULL;
176 if (c->status)
177 mpd_freeStatus(c->status);
178 c->status = NULL;
180 playlist_clear(&c->playlist);
182 if (c->song)
183 c->song = NULL;
185 return 0;
186 }
188 gint
189 mpdclient_connect(mpdclient_t *c,
190 gchar *host,
191 gint port,
192 gfloat _timeout,
193 gchar *password)
194 {
195 gint retval = 0;
197 /* close any open connection */
198 if( c->connection )
199 mpdclient_disconnect(c);
201 /* connect to MPD */
202 c->connection = mpd_newConnection(host, port, _timeout);
203 if( c->connection->error )
204 return error_cb(c, c->connection->error,
205 c->connection->errorStr);
207 /* send password */
208 if( password ) {
209 mpd_sendPasswordCommand(c->connection, password);
210 retval = mpdclient_finish_command(c);
211 }
212 c->need_update = TRUE;
214 return retval;
215 }
217 gint
218 mpdclient_update(mpdclient_t *c)
219 {
220 gint retval = 0;
222 if (MPD_ERROR(c))
223 return -1;
225 /* free the old status */
226 if (c->status)
227 mpd_freeStatus(c->status);
229 /* retreive new status */
230 mpd_sendStatusCommand(c->connection);
231 c->status = mpd_getStatus(c->connection);
232 if ((retval=mpdclient_finish_command(c)))
233 return retval;
235 /* check if the playlist needs an update */
236 if (c->playlist.id != c->status->playlist) {
237 if (playlist_is_empty(&c->playlist))
238 retval = mpdclient_playlist_update_changes(c);
239 else
240 retval = mpdclient_playlist_update(c);
241 }
243 /* update the current song */
244 if (!c->song || c->status->songid != c->song->id) {
245 c->song = playlist_get_song(c, c->status->song);
246 }
248 c->need_update = FALSE;
250 return retval;
251 }
254 /****************************************************************************/
255 /*** MPD Commands **********************************************************/
256 /****************************************************************************/
258 gint
259 mpdclient_cmd_play(mpdclient_t *c, gint idx)
260 {
261 #ifdef ENABLE_SONG_ID
262 struct mpd_song *song = playlist_get_song(c, idx);
264 if (song)
265 mpd_sendPlayIdCommand(c->connection, song->id);
266 else
267 mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
268 #else
269 mpd_sendPlayCommand(c->connection, idx);
270 #endif
271 c->need_update = TRUE;
272 return mpdclient_finish_command(c);
273 }
275 gint
276 mpdclient_cmd_pause(mpdclient_t *c, gint value)
277 {
278 mpd_sendPauseCommand(c->connection, value);
279 return mpdclient_finish_command(c);
280 }
282 gint
283 mpdclient_cmd_crop(mpdclient_t *c)
284 {
285 mpd_Status *status;
286 int length;
288 mpd_sendStatusCommand(c->connection);
289 status = mpd_getStatus(c->connection);
290 length = status->playlistLength - 1;
292 if (length <= 0) {
293 mpd_freeStatus(status);
294 } else if (status->state == 3 || status->state == 2) {
295 /* If playing or paused */
297 mpd_sendCommandListBegin( c->connection );
299 while (length >= 0) {
300 if (length != status->song)
301 mpd_sendDeleteCommand(c->connection, length);
303 length--;
304 }
306 mpd_sendCommandListEnd(c->connection);
307 mpd_freeStatus(status);
308 } else {
309 mpd_freeStatus(status);
310 }
312 return mpdclient_finish_command(c);
313 }
315 gint
316 mpdclient_cmd_stop(mpdclient_t *c)
317 {
318 mpd_sendStopCommand(c->connection);
319 return mpdclient_finish_command(c);
320 }
322 gint
323 mpdclient_cmd_next(mpdclient_t *c)
324 {
325 mpd_sendNextCommand(c->connection);
326 c->need_update = TRUE;
327 return mpdclient_finish_command(c);
328 }
330 gint
331 mpdclient_cmd_prev(mpdclient_t *c)
332 {
333 mpd_sendPrevCommand(c->connection);
334 c->need_update = TRUE;
335 return mpdclient_finish_command(c);
336 }
338 gint
339 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
340 {
341 mpd_sendSeekIdCommand(c->connection, id, pos);
342 return mpdclient_finish_command(c);
343 }
345 gint
346 mpdclient_cmd_shuffle(mpdclient_t *c)
347 {
348 mpd_sendShuffleCommand(c->connection);
349 c->need_update = TRUE;
350 return mpdclient_finish_command(c);
351 }
353 gint
354 mpdclient_cmd_clear(mpdclient_t *c)
355 {
356 gint retval = 0;
358 mpd_sendClearCommand(c->connection);
359 retval = mpdclient_finish_command(c);
360 /* call playlist updated callback */
361 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
362 c->need_update = TRUE;
363 return retval;
364 }
366 gint
367 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
368 {
369 mpd_sendRepeatCommand(c->connection, value);
370 return mpdclient_finish_command(c);
371 }
373 gint
374 mpdclient_cmd_random(mpdclient_t *c, gint value)
375 {
376 mpd_sendRandomCommand(c->connection, value);
377 return mpdclient_finish_command(c);
378 }
380 gint
381 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
382 {
383 mpd_sendCrossfadeCommand(c->connection, value);
384 return mpdclient_finish_command(c);
385 }
387 gint
388 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
389 {
390 mpd_sendUpdateCommand(c->connection, path ? path : "");
391 return mpdclient_finish_command(c);
392 }
394 gint
395 mpdclient_cmd_volume(mpdclient_t *c, gint value)
396 {
397 mpd_sendSetvolCommand(c->connection, value);
398 return mpdclient_finish_command(c);
399 }
401 gint
402 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
403 {
404 mpd_sendAddCommand(c->connection, path_utf8);
405 return mpdclient_finish_command(c);
406 }
408 gint
409 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
410 {
411 gint retval;
412 gchar *path_utf8 = locale_to_utf8(path);
414 retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
415 g_free(path_utf8);
416 return retval;
417 }
419 gint
420 mpdclient_cmd_add(mpdclient_t *c, struct mpd_song *song)
421 {
422 gint retval = 0;
424 if( !song || !song->file )
425 return -1;
427 /* send the add command to mpd */
428 mpd_sendAddCommand(c->connection, song->file);
429 if( (retval=mpdclient_finish_command(c)) )
430 return retval;
432 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
433 /* add the song to playlist */
434 playlist_append(&c->playlist, song);
436 /* increment the playlist id, so we dont retrives a new playlist */
437 c->playlist.id++;
439 /* call playlist updated callback */
440 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
441 #else
442 c->need_update = TRUE;
443 #endif
445 return 0;
446 }
448 gint
449 mpdclient_cmd_delete(mpdclient_t *c, gint idx)
450 {
451 gint retval = 0;
452 struct mpd_song *song;
454 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
455 return -1;
457 song = playlist_get(&c->playlist, idx);
459 /* send the delete command to mpd */
460 #ifdef ENABLE_SONG_ID
461 mpd_sendDeleteIdCommand(c->connection, song->id);
462 #else
463 mpd_sendDeleteCommand(c->connection, idx);
464 #endif
465 if( (retval=mpdclient_finish_command(c)) )
466 return retval;
468 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
469 /* increment the playlist id, so we dont retrive a new playlist */
470 c->playlist.id++;
472 /* remove the song from the playlist */
473 playlist_remove_reuse(&c->playlist, idx);
475 /* call playlist updated callback */
476 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
478 /* remove references to the song */
479 if (c->song == song) {
480 c->song = NULL;
481 c->need_update = TRUE;
482 }
484 mpd_freeSong(song);
486 #else
487 c->need_update = TRUE;
488 #endif
490 return 0;
491 }
493 gint
494 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
495 {
496 gint n;
497 struct mpd_song *song1, *song2;
499 if (old_index == new_index || new_index < 0 ||
500 (guint)new_index >= c->playlist.list->len)
501 return -1;
503 song1 = playlist_get(&c->playlist, old_index);
504 song2 = playlist_get(&c->playlist, new_index);
506 /* send the move command to mpd */
507 #ifdef ENABLE_SONG_ID
508 mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
509 #else
510 mpd_sendMoveCommand(c->connection, old_index, new_index);
511 #endif
512 if( (n=mpdclient_finish_command(c)) )
513 return n;
515 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
516 /* update the playlist */
517 playlist_swap(&c->playlist, old_index, new_index);
519 /* increment the playlist id, so we dont retrives a new playlist */
520 c->playlist.id++;
522 #else
523 c->need_update = TRUE;
524 #endif
526 /* call playlist updated callback */
527 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
529 return 0;
530 }
532 gint
533 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
534 {
535 gint retval = 0;
537 mpd_sendSaveCommand(c->connection, filename_utf8);
538 if ((retval = mpdclient_finish_command(c)) == 0)
539 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
540 return retval;
541 }
543 gint
544 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
545 {
546 gint retval = 0;
547 gchar *filename_utf8 = locale_to_utf8(filename);
549 retval = mpdclient_cmd_save_playlist_utf8(c, filename);
550 g_free(filename_utf8);
551 return retval;
552 }
554 gint
555 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
556 {
557 mpd_sendLoadCommand(c->connection, filename_utf8);
558 c->need_update = TRUE;
559 return mpdclient_finish_command(c);
560 }
562 gint
563 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
564 {
565 gint retval = 0;
567 mpd_sendRmCommand(c->connection, filename_utf8);
568 if ((retval = mpdclient_finish_command(c)) == 0)
569 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
570 return retval;
571 }
573 gint
574 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
575 {
576 gint retval = 0;
577 gchar *filename_utf8 = locale_to_utf8(filename);
579 retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
580 g_free(filename_utf8);
581 return retval;
582 }
585 /****************************************************************************/
586 /*** Callback managment functions *******************************************/
587 /****************************************************************************/
589 static void
590 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
591 {
592 while (list) {
593 mpdc_list_cb_t fn = list->data;
595 fn(c, event, data);
596 list = list->next;
597 }
598 }
600 void
601 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
602 {
603 do_list_callbacks(c, c->playlist_callbacks, event, data);
604 }
606 void
607 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
608 {
609 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
610 }
612 void
613 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
614 {
615 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
616 }
618 void
619 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
620 {
621 do_list_callbacks(c, c->browse_callbacks, event, data);
622 }
625 void
626 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
627 {
628 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
629 }
631 void
632 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
633 {
634 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
635 }
637 void
638 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
639 {
640 c->error_callbacks = g_list_append(c->error_callbacks, cb);
641 }
643 void
644 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
645 {
646 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
647 }
650 /****************************************************************************/
651 /*** Playlist managment functions *******************************************/
652 /****************************************************************************/
654 /* update playlist */
655 gint
656 mpdclient_playlist_update(mpdclient_t *c)
657 {
658 mpd_InfoEntity *entity;
660 if (MPD_ERROR(c))
661 return -1;
663 playlist_clear(&c->playlist);
665 mpd_sendPlaylistInfoCommand(c->connection,-1);
666 while ((entity = mpd_getNextInfoEntity(c->connection))) {
667 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
668 playlist_append(&c->playlist, entity->info.song);
670 mpd_freeInfoEntity(entity);
671 }
673 c->playlist.id = c->status->playlist;
674 c->song = NULL;
676 /* call playlist updated callbacks */
677 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
679 return mpdclient_finish_command(c);
680 }
682 #ifdef ENABLE_PLCHANGES
684 /* update playlist (plchanges) */
685 gint
686 mpdclient_playlist_update_changes(mpdclient_t *c)
687 {
688 mpd_InfoEntity *entity;
690 if (MPD_ERROR(c))
691 return -1;
693 mpd_sendPlChangesCommand(c->connection, c->playlist.id);
695 while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) {
696 struct mpd_song *song = entity->info.song;
698 if (song->pos >= 0 && (guint)song->pos < c->playlist.list->len) {
699 /* update song */
700 playlist_replace(&c->playlist, song->pos, song);
701 } else {
702 /* add a new song */
703 playlist_append(&c->playlist, song);
704 }
706 mpd_freeInfoEntity(entity);
707 }
709 /* remove trailing songs */
710 while ((guint)c->status->playlistLength < c->playlist.list->len) {
711 guint pos = c->playlist.list->len - 1;
713 /* Remove the last playlist entry */
714 playlist_remove(&c->playlist, pos);
715 }
717 c->song = NULL;
718 c->playlist.id = c->status->playlist;
720 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
722 return 0;
723 }
725 #else
726 gint
727 mpdclient_playlist_update_changes(mpdclient_t *c)
728 {
729 return mpdclient_playlist_update(c);
730 }
731 #endif
734 /****************************************************************************/
735 /*** Filelist functions *****************************************************/
736 /****************************************************************************/
738 mpdclient_filelist_t *
739 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
740 {
741 mpdclient_filelist_t *filelist;
742 mpd_InfoEntity *entity;
743 gchar *path_utf8 = locale_to_utf8(path);
744 gboolean has_dirs_only = TRUE;
746 mpd_sendLsInfoCommand(c->connection, path_utf8);
747 filelist = filelist_new(path);
748 if (path && path[0] && strcmp(path, "/"))
749 /* add a dummy entry for ./.. */
750 filelist_append(filelist, NULL);
752 while ((entity=mpd_getNextInfoEntity(c->connection))) {
753 filelist_append(filelist, entity);
755 if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY) {
756 has_dirs_only = FALSE;
757 }
758 }
760 /* If there's an error, ignore it. We'll return an empty filelist. */
761 mpdclient_finish_command(c);
763 g_free(path_utf8);
765 // If there are only directory entities in the filelist, we sort it
766 if (has_dirs_only)
767 filelist_sort(filelist, compare_filelistentry_dir);
769 return filelist;
770 }
772 mpdclient_filelist_t *
773 mpdclient_filelist_search_utf8(mpdclient_t *c,
774 int exact_match,
775 int table,
776 gchar *filter_utf8)
777 {
778 mpdclient_filelist_t *filelist;
779 mpd_InfoEntity *entity;
781 if (exact_match)
782 mpd_sendFindCommand(c->connection, table, filter_utf8);
783 else
784 mpd_sendSearchCommand(c->connection, table, filter_utf8);
785 filelist = filelist_new(NULL);
787 while ((entity=mpd_getNextInfoEntity(c->connection)))
788 filelist_append(filelist, entity);
790 if (mpdclient_finish_command(c)) {
791 filelist_free(filelist);
792 return NULL;
793 }
795 return filelist;
796 }
799 mpdclient_filelist_t *
800 mpdclient_filelist_search(mpdclient_t *c,
801 int exact_match,
802 int table,
803 gchar *_filter)
804 {
805 mpdclient_filelist_t *filelist;
806 gchar *filter_utf8 = locale_to_utf8(_filter);
808 filelist = mpdclient_filelist_search_utf8(c, exact_match, table,
809 filter_utf8);
810 g_free(filter_utf8);
812 return filelist;
813 }
815 mpdclient_filelist_t *
816 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
817 {
818 if (filelist != NULL) {
819 gchar *path = g_strdup(filelist->path);
821 filelist_free(filelist);
822 filelist = mpdclient_filelist_get(c, path);
823 g_free(path);
824 return filelist;
825 }
826 return NULL;
827 }
829 int
830 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
831 {
832 guint i;
834 if (filelist_is_empty(fl))
835 return 0;
837 mpd_sendCommandListBegin(c->connection);
839 for (i = 0; i < filelist_length(fl); ++i) {
840 filelist_entry_t *entry = filelist_get(fl, i);
841 mpd_InfoEntity *entity = entry->entity;
843 if (entity && entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
844 struct mpd_song *song = entity->info.song;
846 mpd_sendAddCommand(c->connection, song->file);
847 }
848 }
850 mpd_sendCommandListEnd(c->connection);
851 return mpdclient_finish_command(c);
852 }
854 GList *
855 mpdclient_get_artists_utf8(mpdclient_t *c)
856 {
857 gchar *str = NULL;
858 GList *list = NULL;
860 mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
861 while ((str = mpd_getNextArtist(c->connection)))
862 list = g_list_append(list, (gpointer) str);
864 if (mpdclient_finish_command(c))
865 return string_list_free(list);
867 return list;
868 }
870 GList *
871 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
872 {
873 gchar *str = NULL;
874 GList *list = NULL;
876 mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
877 while ((str = mpd_getNextAlbum(c->connection)))
878 list = g_list_append(list, (gpointer) str);
880 if (mpdclient_finish_command(c))
881 return string_list_free(list);
883 return list;
884 }