Code

removed LIST_FORMAT, STATUS_FORMAT
[ncmpc.git] / src / mpdclient.c
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 "support.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;
115 /****************************************************************************/
116 /*** mpdclient functions ****************************************************/
117 /****************************************************************************/
119 gint
120 mpdclient_finish_command(mpdclient_t *c)
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;
145 mpdclient_t *
146 mpdclient_new(void)
148         mpdclient_t *c;
150         c = g_malloc0(sizeof(mpdclient_t));
151         playlist_init(&c->playlist);
153         return c;
156 void
157 mpdclient_free(mpdclient_t *c)
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);
169 gint
170 mpdclient_disconnect(mpdclient_t *c)
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;
188 gint
189 mpdclient_connect(mpdclient_t *c,
190                   gchar *host,
191                   gint port,
192                   gfloat _timeout,
193                   gchar *password)
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;
217 gint
218 mpdclient_update(mpdclient_t *c)
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;
254 /****************************************************************************/
255 /*** MPD Commands  **********************************************************/
256 /****************************************************************************/
258 gint
259 mpdclient_cmd_play(mpdclient_t *c, gint idx)
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);
275 gint
276 mpdclient_cmd_pause(mpdclient_t *c, gint value)
278         mpd_sendPauseCommand(c->connection, value);
279         return mpdclient_finish_command(c);
282 gint
283 mpdclient_cmd_crop(mpdclient_t *c)
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);
315 gint
316 mpdclient_cmd_stop(mpdclient_t *c)
318         mpd_sendStopCommand(c->connection);
319         return mpdclient_finish_command(c);
322 gint
323 mpdclient_cmd_next(mpdclient_t *c)
325         mpd_sendNextCommand(c->connection);
326         c->need_update = TRUE;
327         return mpdclient_finish_command(c);
330 gint
331 mpdclient_cmd_prev(mpdclient_t *c)
333         mpd_sendPrevCommand(c->connection);
334         c->need_update = TRUE;
335         return mpdclient_finish_command(c);
338 gint
339 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
341         mpd_sendSeekIdCommand(c->connection, id, pos);
342         return mpdclient_finish_command(c);
345 gint
346 mpdclient_cmd_shuffle(mpdclient_t *c)
348         mpd_sendShuffleCommand(c->connection);
349         c->need_update = TRUE;
350         return mpdclient_finish_command(c);
353 gint
354 mpdclient_cmd_clear(mpdclient_t *c)
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;
366 gint
367 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
369         mpd_sendRepeatCommand(c->connection, value);
370         return mpdclient_finish_command(c);
373 gint
374 mpdclient_cmd_random(mpdclient_t *c, gint value)
376         mpd_sendRandomCommand(c->connection, value);
377         return mpdclient_finish_command(c);
380 gint
381 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
383         mpd_sendCrossfadeCommand(c->connection, value);
384         return mpdclient_finish_command(c);
387 gint
388 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
390         mpd_sendUpdateCommand(c->connection, path ? path : "");
391         return mpdclient_finish_command(c);
394 gint
395 mpdclient_cmd_volume(mpdclient_t *c, gint value)
397         mpd_sendSetvolCommand(c->connection, value);
398         return mpdclient_finish_command(c);
401 gint
402 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
404         mpd_sendAddCommand(c->connection, path_utf8);
405         return mpdclient_finish_command(c);
408 gint
409 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
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;
419 gint
420 mpdclient_cmd_add(mpdclient_t *c, struct mpd_song *song)
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;
448 gint
449 mpdclient_cmd_delete(mpdclient_t *c, gint idx)
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;
493 gint
494 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
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;
532 gint
533 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
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;
543 gint
544 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
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;
554 gint
555 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
557         mpd_sendLoadCommand(c->connection, filename_utf8);
558         c->need_update = TRUE;
559         return mpdclient_finish_command(c);
562 gint
563 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
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;
573 gint
574 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
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;
585 /****************************************************************************/
586 /*** Callback managment functions *******************************************/
587 /****************************************************************************/
589 static void
590 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
592         while (list) {
593                 mpdc_list_cb_t fn = list->data;
595                 fn(c, event, data);
596                 list = list->next;
597         }
600 void
601 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
603         do_list_callbacks(c, c->playlist_callbacks, event, data);
606 void
607 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
609         c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
612 void
613 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
615         c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
618 void
619 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
621         do_list_callbacks(c, c->browse_callbacks, event, data);
625 void
626 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
628         c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
631 void
632 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
634         c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
637 void
638 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
640         c->error_callbacks = g_list_append(c->error_callbacks, cb);
643 void
644 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
646         c->error_callbacks = g_list_remove(c->error_callbacks, cb);
650 /****************************************************************************/
651 /*** Playlist managment functions *******************************************/
652 /****************************************************************************/
654 /* update playlist */
655 gint
656 mpdclient_playlist_update(mpdclient_t *c)
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);
682 #ifdef ENABLE_PLCHANGES
684 /* update playlist (plchanges) */
685 gint
686 mpdclient_playlist_update_changes(mpdclient_t *c)
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;
725 #else
726 gint
727 mpdclient_playlist_update_changes(mpdclient_t *c)
729         return mpdclient_playlist_update(c);
731 #endif
734 /****************************************************************************/
735 /*** Filelist functions *****************************************************/
736 /****************************************************************************/
738 mpdclient_filelist_t *
739 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
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;
772 mpdclient_filelist_t *
773 mpdclient_filelist_search_utf8(mpdclient_t *c,
774                                int exact_match,
775                                int table,
776                                gchar *filter_utf8)
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;
799 mpdclient_filelist_t *
800 mpdclient_filelist_search(mpdclient_t *c,
801                           int exact_match,
802                           int table,
803                           gchar *_filter)
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;
815 mpdclient_filelist_t *
816 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
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;
829 int
830 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
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);
854 GList *
855 mpdclient_get_artists_utf8(mpdclient_t *c)
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;
870 GList *
871 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
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;