1 /*
2 * $Id$
3 *
4 * (c) 2004 by Kalle Wallin <kaw@linux.se>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
21 #include "mpdclient.h"
22 #include "screen_utils.h"
23 #include "config.h"
24 #include "ncmpc.h"
25 #include "support.h"
26 #include "options.h"
27 #include "strfsong.h"
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <time.h>
32 #include <string.h>
34 #undef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
35 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
36 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
37 #define ENABLE_SONG_ID
38 #define ENABLE_PLCHANGES
40 #define BUFSIZE 1024
42 #define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)
44 /* from utils.c */
45 extern GList *string_list_free(GList *string_list);
48 /* filelist sorting functions */
49 static gint
50 compare_filelistentry_dir(gconstpointer filelist_entry1,
51 gconstpointer filelist_entry2)
52 {
53 const mpd_InfoEntity *e1, *e2;
54 char *key1, *key2;
55 int n = 0;
57 e1 = ((const filelist_entry_t *)filelist_entry1)->entity;
58 e2 = ((const filelist_entry_t *)filelist_entry2)->entity;
60 if (e1 && e2 &&
61 e1->type == MPD_INFO_ENTITY_TYPE_DIRECTORY &&
62 e2->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
63 key1 = g_utf8_collate_key(e1->info.directory->path,-1);
64 key2 = g_utf8_collate_key(e2->info.directory->path,-1);
65 n = strcmp(key1,key2);
66 g_free(key1);
67 g_free(key2);
68 }
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, LIST_FORMAT, e1->info.song);
89 strfsong(key2, BUFSIZE, 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, 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 {
108 mpdc_error_cb_t cb = list->data;
109 if( cb )
110 cb(c, error, msg);
111 list=list->next;
112 }
113 mpd_clearError(c->connection);
114 return error;
115 }
117 #ifndef NDEBUG
118 // Unused ath the moment
119 /*
120 #include "strfsong.h"
122 static gchar *
123 get_song_name(mpd_Song *song)
124 {
125 static gchar name[256];
127 strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
128 return name;
129 }
130 */
131 #endif
133 /****************************************************************************/
134 /*** mpdclient functions ****************************************************/
135 /****************************************************************************/
137 gint
138 mpdclient_finish_command(mpdclient_t *c)
139 {
140 mpd_finishCommand(c->connection);
142 if( c->connection->error )
143 {
144 gchar *msg = locale_to_utf8(c->connection->errorStr);
145 gint error = c->connection->error;
146 if( error == MPD_ERROR_ACK )
147 error = error | (c->connection->errorCode << 8);
148 if( c->connection->errorCode == MPD_ACK_ERROR_PERMISSION )
149 {
150 if(screen_auth(c) == 0) return 0;
151 }
152 error_cb(c, error, msg);
153 g_free(msg);
154 return error;
155 }
157 return 0;
158 }
160 mpdclient_t *
161 mpdclient_new(void)
162 {
163 mpdclient_t *c;
165 c = g_malloc0(sizeof(mpdclient_t));
166 playlist_init(&c->playlist);
168 return c;
169 }
171 mpdclient_t *
172 mpdclient_free(mpdclient_t *c)
173 {
174 mpdclient_disconnect(c);
176 mpdclient_playlist_free(&c->playlist);
178 g_list_free(c->error_callbacks);
179 g_list_free(c->playlist_callbacks);
180 g_list_free(c->browse_callbacks);
181 g_free(c);
183 return NULL;
184 }
186 gint
187 mpdclient_disconnect(mpdclient_t *c)
188 {
189 if (c->connection)
190 mpd_closeConnection(c->connection);
191 c->connection = NULL;
193 if (c->status)
194 mpd_freeStatus(c->status);
195 c->status = NULL;
197 playlist_clear(&c->playlist);
199 if (c->song)
200 c->song = NULL;
202 return 0;
203 }
205 gint
206 mpdclient_connect(mpdclient_t *c,
207 gchar *host,
208 gint port,
209 gfloat _timeout,
210 gchar *password)
211 {
212 gint retval = 0;
214 /* close any open connection */
215 if( c->connection )
216 mpdclient_disconnect(c);
218 /* connect to MPD */
219 c->connection = mpd_newConnection(host, port, _timeout);
220 if( c->connection->error )
221 return error_cb(c, c->connection->error,
222 c->connection->errorStr);
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 if (MPD_ERROR(c))
240 return -1;
242 /* free the old status */
243 if (c->status)
244 mpd_freeStatus(c->status);
246 /* retreive new status */
247 mpd_sendStatusCommand(c->connection);
248 c->status = mpd_getStatus(c->connection);
249 if ((retval=mpdclient_finish_command(c)))
250 return retval;
251 #ifndef NDEBUG
252 if (c->status->error)
253 D("status> %s\n", c->status->error);
254 #endif
256 /* check if the playlist needs an update */
257 if (c->playlist.id != c->status->playlist) {
258 if (playlist_is_empty(&c->playlist))
259 retval = mpdclient_playlist_update_changes(c);
260 else
261 retval = mpdclient_playlist_update(c);
262 }
264 /* update the current song */
265 if (!c->song || c->status->songid != c->song->id) {
266 c->song = playlist_get_song(c, c->status->song);
267 }
269 c->need_update = FALSE;
271 return retval;
272 }
275 /****************************************************************************/
276 /*** MPD Commands **********************************************************/
277 /****************************************************************************/
279 gint
280 mpdclient_cmd_play(mpdclient_t *c, gint idx)
281 {
282 #ifdef ENABLE_SONG_ID
283 struct mpd_song *song = playlist_get_song(c, idx);
285 D("Play id:%d\n", song ? song->id : -1);
286 if (song)
287 mpd_sendPlayIdCommand(c->connection, song->id);
288 else
289 mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
290 #else
291 mpd_sendPlayCommand(c->connection, idx);
292 #endif
293 c->need_update = TRUE;
294 return mpdclient_finish_command(c);
295 }
297 gint
298 mpdclient_cmd_pause(mpdclient_t *c, gint value)
299 {
300 mpd_sendPauseCommand(c->connection, value);
301 return mpdclient_finish_command(c);
302 }
304 gint
305 mpdclient_cmd_stop(mpdclient_t *c)
306 {
307 mpd_sendStopCommand(c->connection);
308 return mpdclient_finish_command(c);
309 }
311 gint
312 mpdclient_cmd_next(mpdclient_t *c)
313 {
314 mpd_sendNextCommand(c->connection);
315 c->need_update = TRUE;
316 return mpdclient_finish_command(c);
317 }
319 gint
320 mpdclient_cmd_prev(mpdclient_t *c)
321 {
322 mpd_sendPrevCommand(c->connection);
323 c->need_update = TRUE;
324 return mpdclient_finish_command(c);
325 }
327 gint
328 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
329 {
330 D("Seek id:%d\n", id);
331 mpd_sendSeekIdCommand(c->connection, id, pos);
332 return mpdclient_finish_command(c);
333 }
335 gint
336 mpdclient_cmd_shuffle(mpdclient_t *c)
337 {
338 mpd_sendShuffleCommand(c->connection);
339 c->need_update = TRUE;
340 return mpdclient_finish_command(c);
341 }
343 gint
344 mpdclient_cmd_clear(mpdclient_t *c)
345 {
346 gint retval = 0;
348 mpd_sendClearCommand(c->connection);
349 retval = mpdclient_finish_command(c);
350 /* call playlist updated callback */
351 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
352 c->need_update = TRUE;
353 return retval;
354 }
356 gint
357 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
358 {
359 mpd_sendRepeatCommand(c->connection, value);
360 return mpdclient_finish_command(c);
361 }
363 gint
364 mpdclient_cmd_random(mpdclient_t *c, gint value)
365 {
366 mpd_sendRandomCommand(c->connection, value);
367 return mpdclient_finish_command(c);
368 }
370 gint
371 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
372 {
373 mpd_sendCrossfadeCommand(c->connection, value);
374 return mpdclient_finish_command(c);
375 }
377 gint
378 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
379 {
380 mpd_sendUpdateCommand(c->connection, path ? path : "");
381 return mpdclient_finish_command(c);
382 }
384 gint
385 mpdclient_cmd_volume(mpdclient_t *c, gint value)
386 {
387 mpd_sendSetvolCommand(c->connection, value);
388 return mpdclient_finish_command(c);
389 }
391 gint
392 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
393 {
394 mpd_sendAddCommand(c->connection, path_utf8);
395 return mpdclient_finish_command(c);
396 }
398 gint
399 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
400 {
401 gint retval;
402 gchar *path_utf8 = locale_to_utf8(path);
404 retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
405 g_free(path_utf8);
406 return retval;
407 }
409 gint
410 mpdclient_cmd_add(mpdclient_t *c, struct mpd_song *song)
411 {
412 gint retval = 0;
414 if( !song || !song->file )
415 return -1;
417 /* send the add command to mpd */
418 mpd_sendAddCommand(c->connection, song->file);
419 if( (retval=mpdclient_finish_command(c)) )
420 return retval;
422 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
423 /* add the song to playlist */
424 playlist_append(&c->playlist, song);
426 /* increment the playlist id, so we dont retrives a new playlist */
427 c->playlist.id++;
429 /* call playlist updated callback */
430 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
431 #else
432 c->need_update = TRUE;
433 #endif
435 return 0;
436 }
438 gint
439 mpdclient_cmd_delete(mpdclient_t *c, gint idx)
440 {
441 gint retval = 0;
442 struct mpd_song *song;
444 if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
445 return -1;
447 song = playlist_get(&c->playlist, idx);
449 /* send the delete command to mpd */
450 #ifdef ENABLE_SONG_ID
451 D("Delete id:%d\n", song->id);
452 mpd_sendDeleteIdCommand(c->connection, song->id);
453 #else
454 mpd_sendDeleteCommand(c->connection, idx);
455 #endif
456 if( (retval=mpdclient_finish_command(c)) )
457 return retval;
459 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
460 /* increment the playlist id, so we dont retrive a new playlist */
461 c->playlist.id++;
463 /* remove the song from the playlist */
464 playlist_remove(&c->playlist, idx);
466 /* call playlist updated callback */
467 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
469 /* remove references to the song */
470 if (c->song == song) {
471 c->song = NULL;
472 c->need_update = TRUE;
473 }
475 #else
476 c->need_update = TRUE;
477 #endif
479 return 0;
480 }
482 gint
483 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
484 {
485 gint n;
486 struct mpd_song *song1, *song2;
488 if (old_index == new_index || new_index < 0 ||
489 (guint)new_index >= c->playlist.list->len)
490 return -1;
492 song1 = playlist_get(&c->playlist, old_index);
493 song2 = playlist_get(&c->playlist, new_index);
495 /* send the move command to mpd */
496 #ifdef ENABLE_SONG_ID
497 D("Swapping id:%d with id:%d\n", song1->id, song2->id);
498 mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
499 #else
500 D("Moving index %d to id:%d\n", old_index, new_index);
501 mpd_sendMoveCommand(c->connection, old_index, new_index);
502 #endif
503 if( (n=mpdclient_finish_command(c)) )
504 return n;
506 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
507 /* update the playlist */
508 playlist_swap(&c->playlist, old_index, new_index);
510 /* increment the playlist id, so we dont retrives a new playlist */
511 c->playlist.id++;
513 #else
514 c->need_update = TRUE;
515 #endif
517 /* call playlist updated callback */
518 D("move> new_index=%d, old_index=%d\n", new_index, old_index);
519 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
521 return 0;
522 }
524 gint
525 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
526 {
527 gint retval = 0;
529 mpd_sendSaveCommand(c->connection, filename_utf8);
530 if( (retval=mpdclient_finish_command(c)) == 0 )
531 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
532 return retval;
533 }
535 gint
536 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
537 {
538 gint retval = 0;
539 gchar *filename_utf8 = locale_to_utf8(filename);
541 retval = mpdclient_cmd_save_playlist_utf8(c, filename);
542 g_free(filename_utf8);
543 return retval;
544 }
546 gint
547 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
548 {
549 mpd_sendLoadCommand(c->connection, filename_utf8);
550 c->need_update = TRUE;
551 return mpdclient_finish_command(c);
552 }
554 gint
555 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
556 {
557 gint retval = 0;
559 mpd_sendRmCommand(c->connection, filename_utf8);
560 if( (retval=mpdclient_finish_command(c)) == 0 )
561 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
562 return retval;
563 }
565 gint
566 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
567 {
568 gint retval = 0;
569 gchar *filename_utf8 = locale_to_utf8(filename);
571 retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
572 g_free(filename_utf8);
573 return retval;
574 }
577 /****************************************************************************/
578 /*** Callback managment functions *******************************************/
579 /****************************************************************************/
580 static void
581 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
582 {
583 while(list)
584 {
585 mpdc_list_cb_t fn = list->data;
587 fn(c, event, data);
588 list=list->next;
589 }
590 }
592 void
593 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
594 {
595 do_list_callbacks(c, c->playlist_callbacks, event, data);
596 }
598 void
599 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
600 {
601 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
602 }
604 void
605 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
606 {
607 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
608 }
610 void
611 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
612 {
613 do_list_callbacks(c, c->browse_callbacks, event, data);
614 }
617 void
618 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
619 {
620 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
621 }
623 void
624 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
625 {
626 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
627 }
629 void
630 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
631 {
632 c->error_callbacks = g_list_append(c->error_callbacks, cb);
633 }
635 void
636 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
637 {
638 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
639 }
641 /****************************************************************************/
642 /*** Playlist managment functions *******************************************/
643 /****************************************************************************/
646 /* update playlist */
647 gint
648 mpdclient_playlist_update(mpdclient_t *c)
649 {
650 mpd_InfoEntity *entity;
652 D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
654 if (MPD_ERROR(c))
655 return -1;
657 playlist_clear(&c->playlist);
659 mpd_sendPlaylistInfoCommand(c->connection,-1);
660 while ((entity = mpd_getNextInfoEntity(c->connection))) {
661 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG)
662 playlist_append(&c->playlist, entity->info.song);
664 mpd_freeInfoEntity(entity);
665 }
667 c->playlist.id = c->status->playlist;
668 c->song = NULL;
669 c->playlist.updated = TRUE;
671 /* call playlist updated callbacks */
672 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
674 return mpdclient_finish_command(c);
675 }
677 #ifdef ENABLE_PLCHANGES
679 /* update playlist (plchanges) */
680 gint
681 mpdclient_playlist_update_changes(mpdclient_t *c)
682 {
683 mpd_InfoEntity *entity;
685 D("mpdclient_playlist_update_changes() [%lld -> %lld]\n",
686 c->status->playlist, c->playlist.id);
688 if (MPD_ERROR(c))
689 return -1;
691 mpd_sendPlChangesCommand(c->connection, c->playlist.id);
693 while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) {
694 struct mpd_song *song = entity->info.song;
696 if (song->pos >= 0 && (guint)song->pos < c->playlist.list->len) {
697 /* update song */
698 D("updating pos:%d, id=%d - %s\n",
699 song->pos, song->id, song->file);
700 playlist_replace(&c->playlist, song->pos, song);
701 } else {
702 /* add a new song */
703 D("adding song at pos %d\n", song->pos);
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 D("removing song at pos %d\n", pos);
716 playlist_remove(&c->playlist, pos);
717 }
719 c->song = NULL;
720 c->playlist.id = c->status->playlist;
721 c->playlist.updated = TRUE;
723 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
725 return 0;
726 }
728 #else
729 gint
730 mpdclient_playlist_update_changes(mpdclient_t *c)
731 {
732 return mpdclient_playlist_update(c);
733 }
734 #endif
737 /****************************************************************************/
738 /*** Filelist functions *****************************************************/
739 /****************************************************************************/
741 mpdclient_filelist_t *
742 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
743 {
744 GList *list = g_list_first(filelist->list);
746 D("mpdclient_filelist_free()\n");
747 if (list == NULL)
748 return NULL;
749 while (list != NULL) {
750 filelist_entry_t *entry = list->data;
752 if (entry->entity)
753 mpd_freeInfoEntity(entry->entity);
754 g_free(entry);
755 list=list->next;
756 }
757 g_list_free(filelist->list);
758 g_free(filelist->path);
759 filelist->path = NULL;
760 filelist->list = NULL;
761 filelist->length = 0;
762 g_free(filelist);
764 return NULL;
765 }
768 mpdclient_filelist_t *
769 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
770 {
771 mpdclient_filelist_t *filelist;
772 mpd_InfoEntity *entity;
773 gchar *path_utf8 = locale_to_utf8(path);
774 gboolean has_dirs_only = TRUE;
776 D("mpdclient_filelist_get(%s)\n", path);
777 mpd_sendLsInfoCommand(c->connection, path_utf8);
778 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
779 if (path && path[0] && strcmp(path, "/")) {
780 /* add a dummy entry for ./.. */
781 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
782 entry->entity = NULL;
783 filelist->list = g_list_append(filelist->list, entry);
784 filelist->length++;
785 }
787 while ((entity=mpd_getNextInfoEntity(c->connection))) {
788 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
790 entry->entity = entity;
791 filelist->list = g_list_append(filelist->list, entry);
792 filelist->length++;
794 if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY) {
795 has_dirs_only = FALSE;
796 }
797 }
799 /* If there's an error, ignore it. We'll return an empty filelist. */
800 mpdclient_finish_command(c);
802 g_free(path_utf8);
803 filelist->path = g_strdup(path);
804 filelist->updated = TRUE;
806 // If there are only directory entities in the filelist, we sort it
807 if (has_dirs_only) {
808 D("mpdclient_filelist_get: only dirs; sorting!\n");
809 filelist->list = g_list_sort(filelist->list, compare_filelistentry_dir);
810 }
812 return filelist;
813 }
815 mpdclient_filelist_t *
816 mpdclient_filelist_search_utf8(mpdclient_t *c,
817 int exact_match,
818 int table,
819 gchar *filter_utf8)
820 {
821 mpdclient_filelist_t *filelist;
822 mpd_InfoEntity *entity;
824 D("mpdclient_filelist_search(%s)\n", filter_utf8);
825 if (exact_match)
826 mpd_sendFindCommand(c->connection, table, filter_utf8);
827 else
828 mpd_sendSearchCommand(c->connection, table, filter_utf8);
829 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
831 while ((entity=mpd_getNextInfoEntity(c->connection))) {
832 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
834 entry->entity = entity;
835 filelist->list = g_list_append(filelist->list, entry);
836 filelist->length++;
837 }
839 if (mpdclient_finish_command(c))
840 return mpdclient_filelist_free(filelist);
842 filelist->updated = TRUE;
843 return filelist;
844 }
847 mpdclient_filelist_t *
848 mpdclient_filelist_search(mpdclient_t *c,
849 int exact_match,
850 int table,
851 gchar *_filter)
852 {
853 mpdclient_filelist_t *filelist;
854 gchar *filter_utf8 = locale_to_utf8(_filter);
856 D("mpdclient_filelist_search(%s)\n", _filter);
857 filelist = mpdclient_filelist_search_utf8(c, exact_match, table,
858 filter_utf8);
859 g_free(filter_utf8);
861 return filelist;
862 }
864 mpdclient_filelist_t *
865 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
866 {
867 if( filelist != NULL )
868 {
869 gchar *path = g_strdup(filelist->path);
871 filelist = mpdclient_filelist_free(filelist);
872 filelist = mpdclient_filelist_get(c, path);
873 g_free(path);
874 return filelist;
875 }
876 return NULL;
877 }
879 filelist_entry_t *
880 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, struct mpd_song *song)
881 {
882 GList *list = g_list_first(fl->list);
884 while( list && song)
885 {
886 filelist_entry_t *entry = list->data;
887 mpd_InfoEntity *entity = entry->entity;
889 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
890 {
891 struct mpd_song *song2 = entity->info.song;
893 if( strcmp(song->file, song2->file) == 0 )
894 {
895 return entry;
896 }
897 }
898 list = list->next;
899 }
900 return NULL;
901 }
903 int
904 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
905 {
906 GList *list = g_list_first(fl->list);
908 if( fl->list==NULL || fl->length<1 )
909 return 0;
911 mpd_sendCommandListBegin(c->connection);
912 while( list )
913 {
914 filelist_entry_t *entry = list->data;
915 mpd_InfoEntity *entity = entry->entity;
917 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
918 {
919 struct mpd_song *song = entity->info.song;
921 mpd_sendAddCommand(c->connection, song->file);
922 }
923 list = list->next;
924 }
925 mpd_sendCommandListEnd(c->connection);
926 return mpdclient_finish_command(c);
927 }
936 GList *
937 mpdclient_get_artists_utf8(mpdclient_t *c)
938 {
939 gchar *str = NULL;
940 GList *list = NULL;
942 D("mpdclient_get_artists()\n");
943 mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
944 while( (str=mpd_getNextArtist(c->connection)) )
945 {
946 list = g_list_append(list, (gpointer) str);
947 }
948 if( mpdclient_finish_command(c) )
949 {
950 return string_list_free(list);
951 }
953 return list;
954 }
956 GList *
957 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
958 {
959 gchar *str = NULL;
960 GList *list = NULL;
962 D("mpdclient_get_albums(%s)\n", artist_utf8);
963 mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
964 while( (str=mpd_getNextAlbum(c->connection)) )
965 {
966 list = g_list_append(list, (gpointer) str);
967 }
968 if( mpdclient_finish_command(c) )
969 {
970 return string_list_free(list);
971 }
973 return list;
974 }