0897e8607471bba86dbee1bf26dced9e7527b5f2
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 "screen_utils.h"
22 #include "config.h"
23 #include "options.h"
24 #include "strfsong.h"
25 #include "utils.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 static bool
41 MPD_ERROR(const struct mpdclient *client)
42 {
43 return client->connection == NULL;
44 }
46 /* filelist sorting functions */
47 static gint
48 compare_filelistentry(gconstpointer filelist_entry1,
49 gconstpointer filelist_entry2)
50 {
51 const mpd_InfoEntity *e1, *e2;
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 && e1->type == e2->type) {
58 switch (e1->type) {
59 case MPD_INFO_ENTITY_TYPE_DIRECTORY:
60 n = g_utf8_collate(e1->info.directory->path,
61 e2->info.directory->path);
62 break;
63 case MPD_INFO_ENTITY_TYPE_SONG:
64 break;
65 case MPD_INFO_ENTITY_TYPE_PLAYLISTFILE:
66 n = g_utf8_collate(e1->info.playlistFile->path,
67 e2->info.playlistFile->path);
68 }
69 }
70 return n;
71 }
73 /* sort by list-format */
74 gint
75 compare_filelistentry_format(gconstpointer filelist_entry1,
76 gconstpointer filelist_entry2)
77 {
78 const mpd_InfoEntity *e1, *e2;
79 char key1[BUFSIZE], key2[BUFSIZE];
80 int n = 0;
82 e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
83 e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
85 if (e1 && e2 &&
86 e1->type == MPD_INFO_ENTITY_TYPE_SONG &&
87 e2->type == MPD_INFO_ENTITY_TYPE_SONG) {
88 strfsong(key1, BUFSIZE, options.list_format, e1->info.song);
89 strfsong(key2, BUFSIZE, options.list_format, e2->info.song);
90 n = strcmp(key1,key2);
91 }
93 return n;
94 }
97 /* Error callbacks */
98 static gint
99 error_cb(mpdclient_t *c, gint error, const gchar *msg)
100 {
101 GList *list = c->error_callbacks;
103 if (list == NULL)
104 fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
106 while (list) {
107 mpdc_error_cb_t cb = list->data;
108 if (cb)
109 cb(c, error, msg);
110 list = list->next;
111 }
113 mpd_clearError(c->connection);
114 return error;
115 }
118 /****************************************************************************/
119 /*** mpdclient functions ****************************************************/
120 /****************************************************************************/
122 static gint
123 mpdclient_handle_error(mpdclient_t *c)
124 {
125 enum mpd_error error = c->connection->error;
126 bool is_fatal = error != MPD_ERROR_ACK;
128 if (error == MPD_ERROR_SUCCESS)
129 return 0;
131 if (error == MPD_ERROR_ACK &&
132 c->connection->errorCode == MPD_ACK_ERROR_PERMISSION &&
133 screen_auth(c) == 0)
134 return 0;
136 if (error == MPD_ERROR_ACK)
137 error = error | (c->connection->errorCode << 8);
139 error_cb(c, error, c->connection->errorStr);
141 if (is_fatal)
142 mpdclient_disconnect(c);
144 return error;
145 }
147 gint
148 mpdclient_finish_command(mpdclient_t *c)
149 {
150 mpd_finishCommand(c->connection);
151 return mpdclient_handle_error(c);
152 }
154 mpdclient_t *
155 mpdclient_new(void)
156 {
157 mpdclient_t *c;
159 c = g_malloc0(sizeof(mpdclient_t));
160 playlist_init(&c->playlist);
161 c->volume = MPD_STATUS_NO_VOLUME;
163 return c;
164 }
166 void
167 mpdclient_free(mpdclient_t *c)
168 {
169 mpdclient_disconnect(c);
171 mpdclient_playlist_free(&c->playlist);
173 g_list_free(c->error_callbacks);
174 g_list_free(c->playlist_callbacks);
175 g_list_free(c->browse_callbacks);
176 g_free(c);
177 }
179 gint
180 mpdclient_disconnect(mpdclient_t *c)
181 {
182 if (c->connection)
183 mpd_closeConnection(c->connection);
184 c->connection = NULL;
186 if (c->status)
187 mpd_freeStatus(c->status);
188 c->status = NULL;
190 playlist_clear(&c->playlist);
192 if (c->song)
193 c->song = NULL;
195 return 0;
196 }
198 gint
199 mpdclient_connect(mpdclient_t *c,
200 const gchar *host,
201 gint port,
202 gfloat _timeout,
203 const gchar *password)
204 {
205 gint retval = 0;
207 /* close any open connection */
208 if( c->connection )
209 mpdclient_disconnect(c);
211 /* connect to MPD */
212 c->connection = mpd_newConnection(host, port, _timeout);
213 if (c->connection->error) {
214 retval = error_cb(c, c->connection->error,
215 c->connection->errorStr);
216 if (retval != 0) {
217 mpd_closeConnection(c->connection);
218 c->connection = NULL;
219 }
221 return retval;
222 }
224 /* send password */
225 if( password ) {
226 mpd_sendPasswordCommand(c->connection, password);
227 retval = mpdclient_finish_command(c);
228 }
229 c->need_update = TRUE;
231 return retval;
232 }
234 gint
235 mpdclient_update(mpdclient_t *c)
236 {
237 gint retval = 0;
239 c->volume = MPD_STATUS_NO_VOLUME;
241 if (MPD_ERROR(c))
242 return -1;
244 /* free the old status */
245 if (c->status)
246 mpd_freeStatus(c->status);
248 /* retrieve new status */
249 mpd_sendStatusCommand(c->connection);
250 c->status = mpd_getStatus(c->connection);
251 if ((retval=mpdclient_finish_command(c)))
252 return retval;
254 if (c->updatingdb && c->updatingdb != c->status->updatingDb)
255 mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
257 c->updatingdb = c->status->updatingDb;
258 c->volume = c->status->volume;
260 /* check if the playlist needs an update */
261 if (c->playlist.id != c->status->playlist) {
262 if (playlist_is_empty(&c->playlist))
263 retval = mpdclient_playlist_update_changes(c);
264 else
265 retval = mpdclient_playlist_update(c);
266 }
268 /* update the current song */
269 if (!c->song || c->status->songid != c->song->id) {
270 c->song = playlist_get_song(c, c->status->song);
271 }
273 c->need_update = FALSE;
275 return retval;
276 }
279 /****************************************************************************/
280 /*** MPD Commands **********************************************************/
281 /****************************************************************************/
283 gint
284 mpdclient_cmd_play(mpdclient_t *c, gint idx)
285 {
286 #ifdef ENABLE_SONG_ID
287 struct mpd_song *song = playlist_get_song(c, idx);
289 if (MPD_ERROR(c))
290 return -1;
292 if (song)
293 mpd_sendPlayIdCommand(c->connection, song->id);
294 else
295 mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
296 #else
297 if (MPD_ERROR(c))
298 return -1;
300 mpd_sendPlayCommand(c->connection, idx);
301 #endif
302 c->need_update = TRUE;
303 return mpdclient_finish_command(c);
304 }
306 gint
307 mpdclient_cmd_pause(mpdclient_t *c, gint value)
308 {
309 if (MPD_ERROR(c))
310 return -1;
312 mpd_sendPauseCommand(c->connection, value);
313 return mpdclient_finish_command(c);
314 }
316 gint
317 mpdclient_cmd_crop(mpdclient_t *c)
318 {
319 gint error;
320 mpd_Status *status;
321 bool playing;
322 int length, current;
324 if (MPD_ERROR(c))
325 return -1;
327 mpd_sendStatusCommand(c->connection);
328 status = mpd_getStatus(c->connection);
329 error = mpdclient_finish_command(c);
330 if (error)
331 return error;
333 playing = status->state == MPD_STATUS_STATE_PLAY ||
334 status->state == MPD_STATUS_STATE_PAUSE;
335 length = status->playlistLength;
336 current = status->song;
338 mpd_freeStatus(status);
340 if (!playing || length < 2)
341 return 0;
343 mpd_sendCommandListBegin( c->connection );
345 while (--length >= 0)
346 if (length != current)
347 mpd_sendDeleteCommand(c->connection, length);
349 mpd_sendCommandListEnd(c->connection);
351 return mpdclient_finish_command(c);
352 }
354 gint
355 mpdclient_cmd_stop(mpdclient_t *c)
356 {
357 if (MPD_ERROR(c))
358 return -1;
360 mpd_sendStopCommand(c->connection);
361 return mpdclient_finish_command(c);
362 }
364 gint
365 mpdclient_cmd_next(mpdclient_t *c)
366 {
367 if (MPD_ERROR(c))
368 return -1;
370 mpd_sendNextCommand(c->connection);
371 c->need_update = TRUE;
372 return mpdclient_finish_command(c);
373 }
375 gint
376 mpdclient_cmd_prev(mpdclient_t *c)
377 {
378 if (MPD_ERROR(c))
379 return -1;
381 mpd_sendPrevCommand(c->connection);
382 c->need_update = TRUE;
383 return mpdclient_finish_command(c);
384 }
386 gint
387 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
388 {
389 if (MPD_ERROR(c))
390 return -1;
392 mpd_sendSeekIdCommand(c->connection, id, pos);
393 return mpdclient_finish_command(c);
394 }
396 gint
397 mpdclient_cmd_shuffle(mpdclient_t *c)
398 {
399 if (MPD_ERROR(c))
400 return -1;
402 mpd_sendShuffleCommand(c->connection);
403 c->need_update = TRUE;
404 return mpdclient_finish_command(c);
405 }
407 gint
408 mpdclient_cmd_shuffle_range(mpdclient_t *c, guint start, guint end)
409 {
410 mpd_sendShuffleRangeCommand(c->connection, start, end);
411 c->need_update = TRUE;
412 return mpdclient_finish_command(c);
413 }
415 gint
416 mpdclient_cmd_clear(mpdclient_t *c)
417 {
418 gint retval = 0;
420 if (MPD_ERROR(c))
421 return -1;
423 mpd_sendClearCommand(c->connection);
424 retval = mpdclient_finish_command(c);
425 /* call playlist updated callback */
426 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
427 c->need_update = TRUE;
428 return retval;
429 }
431 gint
432 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
433 {
434 if (MPD_ERROR(c))
435 return -1;
437 mpd_sendRepeatCommand(c->connection, value);
438 return mpdclient_finish_command(c);
439 }
441 gint
442 mpdclient_cmd_random(mpdclient_t *c, gint value)
443 {
444 if (MPD_ERROR(c))
445 return -1;
447 mpd_sendRandomCommand(c->connection, value);
448 return mpdclient_finish_command(c);
449 }
451 gint
452 mpdclient_cmd_single(mpdclient_t *c, gint value)
453 {
454 if (MPD_ERROR(c))
455 return -1;
457 mpd_sendSingleCommand(c->connection, value);
458 return mpdclient_finish_command(c);
459 }
461 gint
462 mpdclient_cmd_consume(mpdclient_t *c, gint value)
463 {
464 if (MPD_ERROR(c))
465 return -1;
467 mpd_sendConsumeCommand(c->connection, value);
468 return mpdclient_finish_command(c);
469 }
471 gint
472 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
473 {
474 if (MPD_ERROR(c))
475 return -1;
477 mpd_sendCrossfadeCommand(c->connection, value);
478 return mpdclient_finish_command(c);
479 }
481 gint
482 mpdclient_cmd_db_update(mpdclient_t *c, const gchar *path)
483 {
484 gint ret;
486 if (MPD_ERROR(c))
487 return -1;
489 mpd_sendUpdateCommand(c->connection, path ? path : "");
490 ret = mpdclient_finish_command(c);
492 if (ret == 0)
493 /* set updatingDb to make sure the browse callback
494 gets called even if the update has finished before
495 status is updated */
496 c->updatingdb = 1;
498 return ret;
499 }
501 gint
502 mpdclient_cmd_volume(mpdclient_t *c, gint value)
503 {
504 if (MPD_ERROR(c))
505 return -1;
507 mpd_sendSetvolCommand(c->connection, value);
508 return mpdclient_finish_command(c);
509 }
511 gint mpdclient_cmd_volume_up(struct mpdclient *c)
512 {
513 if (MPD_ERROR(c))
514 return -1;
516 if (c->status == NULL || c->status->volume == MPD_STATUS_NO_VOLUME)
517 return 0;
519 if (c->volume == MPD_STATUS_NO_VOLUME)
520 c->volume = c->status->volume;
522 if (c->volume >= 100)
523 return 0;
525 return mpdclient_cmd_volume(c, ++c->volume);
526 }
528 gint mpdclient_cmd_volume_down(struct mpdclient *c)
529 {
530 if (MPD_ERROR(c))
531 return -1;
533 if (c->status == NULL || c->status->volume == MPD_STATUS_NO_VOLUME)
534 return 0;
536 if (c->volume == MPD_STATUS_NO_VOLUME)
537 c->volume = c->status->volume;
539 if (c->volume <= 0)
540 return 0;
542 return mpdclient_cmd_volume(c, --c->volume);
543 }
545 gint
546 mpdclient_cmd_add_path(mpdclient_t *c, const gchar *path_utf8)
547 {
548 if (MPD_ERROR(c))
549 return -1;
551 mpd_sendAddCommand(c->connection, path_utf8);
552 return mpdclient_finish_command(c);
553 }
555 gint
556 mpdclient_cmd_add(mpdclient_t *c, const struct mpd_song *song)
557 {
558 gint retval = 0;
560 if (MPD_ERROR(c))
561 return -1;
563 if( !song || !song->file )
564 return -1;
566 /* send the add command to mpd */
567 mpd_sendAddCommand(c->connection, song->file);
568 if( (retval=mpdclient_finish_command(c)) )
569 return retval;
571 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
572 /* add the song to playlist */
573 playlist_append(&c->playlist, song);
575 /* increment the playlist id, so we don't retrieve a new playlist */
576 c->playlist.id++;
578 /* call playlist updated callback */
579 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
580 #else
581 c->need_update = TRUE;
582 #endif
584 return 0;
585 }
587 gint
588 mpdclient_cmd_delete(mpdclient_t *c, gint idx)
589 {
590 gint retval = 0;
591 struct mpd_song *song;
593 if (MPD_ERROR(c))
594 return -1;
596 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
597 return -1;
599 song = playlist_get(&c->playlist, idx);
601 /* send the delete command to mpd */
602 #ifdef ENABLE_SONG_ID
603 mpd_sendDeleteIdCommand(c->connection, song->id);
604 #else
605 mpd_sendDeleteCommand(c->connection, idx);
606 #endif
607 if( (retval=mpdclient_finish_command(c)) )
608 return retval;
610 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
611 /* increment the playlist id, so we don't retrieve a new playlist */
612 c->playlist.id++;
614 /* remove the song from the playlist */
615 playlist_remove_reuse(&c->playlist, idx);
617 /* call playlist updated callback */
618 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
620 /* remove references to the song */
621 if (c->song == song) {
622 c->song = NULL;
623 c->need_update = TRUE;
624 }
626 mpd_freeSong(song);
628 #else
629 c->need_update = TRUE;
630 #endif
632 return 0;
633 }
635 gint
636 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
637 {
638 gint n;
639 struct mpd_song *song1, *song2;
641 if (MPD_ERROR(c))
642 return -1;
644 if (old_index == new_index || new_index < 0 ||
645 (guint)new_index >= c->playlist.list->len)
646 return -1;
648 song1 = playlist_get(&c->playlist, old_index);
649 song2 = playlist_get(&c->playlist, new_index);
651 /* send the move command to mpd */
652 #ifdef ENABLE_SONG_ID
653 mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
654 #else
655 mpd_sendMoveCommand(c->connection, old_index, new_index);
656 #endif
657 if( (n=mpdclient_finish_command(c)) )
658 return n;
660 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
661 /* update the playlist */
662 playlist_swap(&c->playlist, old_index, new_index);
664 /* increment the playlist id, so we don't retrieve a new playlist */
665 c->playlist.id++;
667 #else
668 c->need_update = TRUE;
669 #endif
671 /* call playlist updated callback */
672 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
674 return 0;
675 }
677 gint
678 mpdclient_cmd_save_playlist(mpdclient_t *c, const gchar *filename_utf8)
679 {
680 gint retval = 0;
682 if (MPD_ERROR(c))
683 return -1;
685 mpd_sendSaveCommand(c->connection, filename_utf8);
686 if ((retval = mpdclient_finish_command(c)) == 0)
687 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
688 return retval;
689 }
691 gint
692 mpdclient_cmd_load_playlist(mpdclient_t *c, const gchar *filename_utf8)
693 {
694 if (MPD_ERROR(c))
695 return -1;
697 mpd_sendLoadCommand(c->connection, filename_utf8);
698 c->need_update = TRUE;
699 return mpdclient_finish_command(c);
700 }
702 gint
703 mpdclient_cmd_delete_playlist(mpdclient_t *c, const gchar *filename_utf8)
704 {
705 gint retval = 0;
707 if (MPD_ERROR(c))
708 return -1;
710 mpd_sendRmCommand(c->connection, filename_utf8);
711 if ((retval = mpdclient_finish_command(c)) == 0)
712 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
713 return retval;
714 }
717 /****************************************************************************/
718 /*** Callback management functions ******************************************/
719 /****************************************************************************/
721 static void
722 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
723 {
724 while (list) {
725 mpdc_list_cb_t fn = list->data;
727 fn(c, event, data);
728 list = list->next;
729 }
730 }
732 void
733 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
734 {
735 do_list_callbacks(c, c->playlist_callbacks, event, data);
736 }
738 void
739 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
740 {
741 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
742 }
744 void
745 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
746 {
747 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
748 }
750 void
751 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
752 {
753 do_list_callbacks(c, c->browse_callbacks, event, data);
754 }
757 void
758 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
759 {
760 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
761 }
763 void
764 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
765 {
766 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
767 }
769 void
770 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
771 {
772 c->error_callbacks = g_list_append(c->error_callbacks, cb);
773 }
775 void
776 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
777 {
778 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
779 }
782 /****************************************************************************/
783 /*** Playlist management functions ******************************************/
784 /****************************************************************************/
786 /* update playlist */
787 gint
788 mpdclient_playlist_update(mpdclient_t *c)
789 {
790 mpd_InfoEntity *entity;
792 if (MPD_ERROR(c))
793 return -1;
795 playlist_clear(&c->playlist);
797 mpd_sendPlaylistInfoCommand(c->connection,-1);
798 while ((entity = mpd_getNextInfoEntity(c->connection))) {
799 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
800 playlist_append(&c->playlist, entity->info.song);
802 mpd_freeInfoEntity(entity);
803 }
805 c->playlist.id = c->status->playlist;
806 c->song = NULL;
808 /* call playlist updated callbacks */
809 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
811 return mpdclient_finish_command(c);
812 }
814 #ifdef ENABLE_PLCHANGES
816 /* update playlist (plchanges) */
817 gint
818 mpdclient_playlist_update_changes(mpdclient_t *c)
819 {
820 mpd_InfoEntity *entity;
822 if (MPD_ERROR(c))
823 return -1;
825 mpd_sendPlChangesCommand(c->connection, c->playlist.id);
827 while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) {
828 struct mpd_song *song = entity->info.song;
830 if (song->pos >= 0 && (guint)song->pos < c->playlist.list->len) {
831 /* update song */
832 playlist_replace(&c->playlist, song->pos, song);
833 } else {
834 /* add a new song */
835 playlist_append(&c->playlist, song);
836 }
838 mpd_freeInfoEntity(entity);
839 }
841 /* remove trailing songs */
842 while ((guint)c->status->playlistLength < c->playlist.list->len) {
843 guint pos = c->playlist.list->len - 1;
845 /* Remove the last playlist entry */
846 playlist_remove(&c->playlist, pos);
847 }
849 c->song = NULL;
850 c->playlist.id = c->status->playlist;
852 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
854 return 0;
855 }
857 #else
858 gint
859 mpdclient_playlist_update_changes(mpdclient_t *c)
860 {
861 return mpdclient_playlist_update(c);
862 }
863 #endif
866 /****************************************************************************/
867 /*** Filelist functions *****************************************************/
868 /****************************************************************************/
870 mpdclient_filelist_t *
871 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
872 {
873 mpdclient_filelist_t *filelist;
874 mpd_InfoEntity *entity;
876 if (MPD_ERROR(c))
877 return NULL;
879 mpd_sendLsInfoCommand(c->connection, path);
880 filelist = filelist_new();
881 if (path && path[0] && strcmp(path, "/"))
882 /* add a dummy entry for ./.. */
883 filelist_append(filelist, NULL);
885 while ((entity=mpd_getNextInfoEntity(c->connection))) {
886 filelist_append(filelist, entity);
887 }
889 /* If there's an error, ignore it. We'll return an empty filelist. */
890 mpdclient_finish_command(c);
892 filelist_sort_dir_play(filelist, compare_filelistentry);
894 return filelist;
895 }
897 mpdclient_filelist_t *
898 mpdclient_filelist_search(mpdclient_t *c,
899 int exact_match,
900 int table,
901 gchar *filter_utf8)
902 {
903 mpdclient_filelist_t *filelist;
904 mpd_InfoEntity *entity;
906 if (MPD_ERROR(c))
907 return NULL;
909 if (exact_match)
910 mpd_sendFindCommand(c->connection, table, filter_utf8);
911 else
912 mpd_sendSearchCommand(c->connection, table, filter_utf8);
913 filelist = filelist_new();
915 while ((entity=mpd_getNextInfoEntity(c->connection)))
916 filelist_append(filelist, entity);
918 if (mpdclient_finish_command(c)) {
919 filelist_free(filelist);
920 return NULL;
921 }
923 return filelist;
924 }
926 int
927 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
928 {
929 guint i;
931 if (MPD_ERROR(c))
932 return -1;
934 if (filelist_is_empty(fl))
935 return 0;
937 mpd_sendCommandListBegin(c->connection);
939 for (i = 0; i < filelist_length(fl); ++i) {
940 filelist_entry_t *entry = filelist_get(fl, i);
941 mpd_InfoEntity *entity = entry->entity;
943 if (entity && entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
944 struct mpd_song *song = entity->info.song;
946 mpd_sendAddCommand(c->connection, song->file);
947 }
948 }
950 mpd_sendCommandListEnd(c->connection);
951 return mpdclient_finish_command(c);
952 }
954 GList *
955 mpdclient_get_artists(mpdclient_t *c)
956 {
957 gchar *str = NULL;
958 GList *list = NULL;
960 if (MPD_ERROR(c))
961 return NULL;
963 mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
964 while ((str = mpd_getNextArtist(c->connection)))
965 list = g_list_append(list, (gpointer) str);
967 if (mpdclient_finish_command(c))
968 return string_list_free(list);
970 return list;
971 }
973 GList *
974 mpdclient_get_albums(mpdclient_t *c, const gchar *artist_utf8)
975 {
976 gchar *str = NULL;
977 GList *list = NULL;
979 if (MPD_ERROR(c))
980 return NULL;
982 mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
983 while ((str = mpd_getNextAlbum(c->connection)))
984 list = g_list_append(list, (gpointer) str);
986 if (mpdclient_finish_command(c))
987 return string_list_free(list);
989 return list;
990 }