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 (c->playlist.list)
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 c->playlist.list = g_list_append(c->playlist.list, mpd_songDup(song));
425 c->playlist.length++;
427 /* increment the playlist id, so we dont retrives a new playlist */
428 c->playlist.id++;
430 /* call playlist updated callback */
431 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
432 #else
433 c->need_update = TRUE;
434 #endif
436 return 0;
437 }
439 gint
440 mpdclient_cmd_delete(mpdclient_t *c, gint idx)
441 {
442 gint retval = 0;
443 struct mpd_song *song = playlist_get_song(c, idx);
445 if( !song )
446 return -1;
448 /* send the delete command to mpd */
449 #ifdef ENABLE_SONG_ID
450 D("Delete id:%d\n", song->id);
451 mpd_sendDeleteIdCommand(c->connection, song->id);
452 #else
453 mpd_sendDeleteCommand(c->connection, idx);
454 #endif
455 if( (retval=mpdclient_finish_command(c)) )
456 return retval;
458 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
459 /* increment the playlist id, so we dont retrive a new playlist */
460 c->playlist.id++;
462 /* remove the song from the playlist */
463 g_array_remove_index(c->playlist.list, idx);
465 /* call playlist updated callback */
466 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
468 /* remove references to the song */
469 if (c->song == song) {
470 c->song = NULL;
471 c->need_update = TRUE;
472 }
474 /* free song */
475 mpd_freeSong(song);
477 #else
478 c->need_update = TRUE;
479 #endif
481 return 0;
482 }
484 gint
485 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
486 {
487 gint n;
488 struct mpd_song *song1, *song2;
490 if (old_index == new_index || new_index < 0 ||
491 (guint)new_index >= c->playlist.list->len)
492 return -1;
494 song1 = playlist_get_song(c, old_index);
495 song2 = playlist_get_song(c, new_index);
497 /* send the move command to mpd */
498 #ifdef ENABLE_SONG_ID
499 D("Swapping id:%d with id:%d\n", song1->id, song2->id);
500 mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
501 #else
502 D("Moving index %d to id:%d\n", old_index, new_index);
503 mpd_sendMoveCommand(c->connection, old_index, new_index);
504 #endif
505 if( (n=mpdclient_finish_command(c)) )
506 return n;
508 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
509 /* update the songs position field */
510 n = song1->pos;
511 song1->pos = song2->pos;
512 song2->pos = n;
513 /* update the array */
514 g_array_index(c->playlist.list, struct mpd_song *, old_index) = song2;
515 g_array_index(c->playlist.list, struct mpd_song *, new_index) = song1;
517 /* increment the playlist id, so we dont retrives a new playlist */
518 c->playlist.id++;
520 #else
521 c->need_update = TRUE;
522 #endif
524 /* call playlist updated callback */
525 D("move> new_index=%d, old_index=%d\n", new_index, old_index);
526 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
528 return 0;
529 }
531 gint
532 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
533 {
534 gint retval = 0;
536 mpd_sendSaveCommand(c->connection, filename_utf8);
537 if( (retval=mpdclient_finish_command(c)) == 0 )
538 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
539 return retval;
540 }
542 gint
543 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
544 {
545 gint retval = 0;
546 gchar *filename_utf8 = locale_to_utf8(filename);
548 retval = mpdclient_cmd_save_playlist_utf8(c, filename);
549 g_free(filename_utf8);
550 return retval;
551 }
553 gint
554 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
555 {
556 mpd_sendLoadCommand(c->connection, filename_utf8);
557 c->need_update = TRUE;
558 return mpdclient_finish_command(c);
559 }
561 gint
562 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
563 {
564 gint retval = 0;
566 mpd_sendRmCommand(c->connection, filename_utf8);
567 if( (retval=mpdclient_finish_command(c)) == 0 )
568 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
569 return retval;
570 }
572 gint
573 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
574 {
575 gint retval = 0;
576 gchar *filename_utf8 = locale_to_utf8(filename);
578 retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
579 g_free(filename_utf8);
580 return retval;
581 }
584 /****************************************************************************/
585 /*** Callback managment functions *******************************************/
586 /****************************************************************************/
587 static void
588 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
589 {
590 while(list)
591 {
592 mpdc_list_cb_t fn = list->data;
594 fn(c, event, data);
595 list=list->next;
596 }
597 }
599 void
600 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
601 {
602 do_list_callbacks(c, c->playlist_callbacks, event, data);
603 }
605 void
606 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
607 {
608 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
609 }
611 void
612 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
613 {
614 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
615 }
617 void
618 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
619 {
620 do_list_callbacks(c, c->browse_callbacks, event, data);
621 }
624 void
625 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
626 {
627 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
628 }
630 void
631 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
632 {
633 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
634 }
636 void
637 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
638 {
639 c->error_callbacks = g_list_append(c->error_callbacks, cb);
640 }
642 void
643 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
644 {
645 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
646 }
648 /****************************************************************************/
649 /*** Playlist managment functions *******************************************/
650 /****************************************************************************/
653 /* update playlist */
654 gint
655 mpdclient_playlist_update(mpdclient_t *c)
656 {
657 mpd_InfoEntity *entity;
659 D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
661 if (MPD_ERROR(c))
662 return -1;
664 if (c->playlist.list)
665 mpdclient_playlist_free(&c->playlist);
667 mpd_sendPlaylistInfoCommand(c->connection,-1);
668 while ((entity = mpd_getNextInfoEntity(c->connection))) {
669 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
670 struct mpd_song *song = mpd_songDup(entity->info.song);
671 g_array_append_val(c->playlist.list, song);
672 }
673 mpd_freeInfoEntity(entity);
674 }
676 c->playlist.id = c->status->playlist;
677 c->song = NULL;
678 c->playlist.updated = TRUE;
680 /* call playlist updated callbacks */
681 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
683 return mpdclient_finish_command(c);
684 }
686 #ifdef ENABLE_PLCHANGES
688 /* update playlist (plchanges) */
689 gint
690 mpdclient_playlist_update_changes(mpdclient_t *c)
691 {
692 mpd_InfoEntity *entity;
694 D("mpdclient_playlist_update_changes() [%lld -> %lld]\n",
695 c->status->playlist, c->playlist.id);
697 if (MPD_ERROR(c))
698 return -1;
700 mpd_sendPlChangesCommand(c->connection, c->playlist.id);
702 while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) {
703 struct mpd_song *song = mpd_songDup(entity->info.song);
705 if (song->pos >= 0 && (guint)song->pos < c->playlist.list->len) {
706 /* update song */
707 D("updating pos:%d, id=%d - %s\n",
708 song->pos, song->id, song->file);
709 mpd_freeSong(g_array_index(c->playlist.list,
710 struct mpd_song *, song->pos));
711 g_array_index(c->playlist.list,
712 struct mpd_song *, song->pos) = song;
713 } else {
714 /* add a new song */
715 D("adding song at pos %d\n", song->pos);
716 g_array_append_val(c->playlist.list, song);
717 }
719 mpd_freeInfoEntity(entity);
720 }
722 /* remove trailing songs */
723 while ((guint)c->status->playlistLength < c->playlist.list->len) {
724 guint pos = c->playlist.list->len - 1;
725 struct mpd_song *song = g_array_index(c->playlist.list, struct mpd_song *, pos);
727 /* Remove the last playlist entry */
728 D("removing song at pos %d\n", pos);
729 mpd_freeSong(song);
730 g_array_remove_index(c->playlist.list, pos);
731 }
733 c->song = NULL;
734 c->playlist.id = c->status->playlist;
735 c->playlist.updated = TRUE;
737 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
739 return 0;
740 }
742 #else
743 gint
744 mpdclient_playlist_update_changes(mpdclient_t *c)
745 {
746 return mpdclient_playlist_update(c);
747 }
748 #endif
751 /****************************************************************************/
752 /*** Filelist functions *****************************************************/
753 /****************************************************************************/
755 mpdclient_filelist_t *
756 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
757 {
758 GList *list = g_list_first(filelist->list);
760 D("mpdclient_filelist_free()\n");
761 if( list == NULL )
762 return NULL;
763 while( list!=NULL )
764 {
765 filelist_entry_t *entry = list->data;
767 if( entry->entity )
768 mpd_freeInfoEntity(entry->entity);
769 g_free(entry);
770 list=list->next;
771 }
772 g_list_free(filelist->list);
773 g_free(filelist->path);
774 filelist->path = NULL;
775 filelist->list = NULL;
776 filelist->length = 0;
777 g_free(filelist);
779 return NULL;
780 }
783 mpdclient_filelist_t *
784 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
785 {
786 mpdclient_filelist_t *filelist;
787 mpd_InfoEntity *entity;
788 gchar *path_utf8 = locale_to_utf8(path);
789 gboolean has_dirs_only = TRUE;
791 D("mpdclient_filelist_get(%s)\n", path);
792 mpd_sendLsInfoCommand(c->connection, path_utf8);
793 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
794 if( path && path[0] && strcmp(path, "/") )
795 {
796 /* add a dummy entry for ./.. */
797 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
798 entry->entity = NULL;
799 filelist->list = g_list_append(filelist->list, (gpointer) entry);
800 filelist->length++;
801 }
803 while( (entity=mpd_getNextInfoEntity(c->connection)) )
804 {
805 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
807 entry->entity = entity;
808 filelist->list = g_list_append(filelist->list, (gpointer) entry);
809 filelist->length++;
811 if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY)
812 {
813 has_dirs_only = FALSE;
814 }
815 }
817 /* If there's an error, ignore it. We'll return an empty filelist. */
818 mpdclient_finish_command(c);
820 g_free(path_utf8);
821 filelist->path = g_strdup(path);
822 filelist->updated = TRUE;
824 // If there are only directory entities in the filelist, we sort it
825 if (has_dirs_only)
826 {
827 D("mpdclient_filelist_get: only dirs; sorting!\n");
828 filelist->list = g_list_sort(filelist->list, compare_filelistentry_dir);
829 }
831 return filelist;
832 }
834 mpdclient_filelist_t *
835 mpdclient_filelist_search_utf8(mpdclient_t *c,
836 int exact_match,
837 int table,
838 gchar *filter_utf8)
839 {
840 mpdclient_filelist_t *filelist;
841 mpd_InfoEntity *entity;
843 D("mpdclient_filelist_search(%s)\n", filter_utf8);
844 if( exact_match )
845 mpd_sendFindCommand(c->connection, table, filter_utf8);
846 else
847 mpd_sendSearchCommand(c->connection, table, filter_utf8);
848 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
850 while( (entity=mpd_getNextInfoEntity(c->connection)) )
851 {
852 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
854 entry->entity = entity;
855 filelist->list = g_list_append(filelist->list, (gpointer) entry);
856 filelist->length++;
857 }
859 if( mpdclient_finish_command(c) )
860 return mpdclient_filelist_free(filelist);
862 filelist->updated = TRUE;
864 return filelist;
865 }
868 mpdclient_filelist_t *
869 mpdclient_filelist_search(mpdclient_t *c,
870 int exact_match,
871 int table,
872 gchar *_filter)
873 {
874 mpdclient_filelist_t *filelist;
875 gchar *filter_utf8 = locale_to_utf8(_filter);
877 D("mpdclient_filelist_search(%s)\n", _filter);
878 filelist = mpdclient_filelist_search_utf8(c, exact_match, table,
879 filter_utf8);
880 g_free(filter_utf8);
882 return filelist;
883 }
885 mpdclient_filelist_t *
886 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
887 {
888 if( filelist != NULL )
889 {
890 gchar *path = g_strdup(filelist->path);
892 filelist = mpdclient_filelist_free(filelist);
893 filelist = mpdclient_filelist_get(c, path);
894 g_free(path);
895 return filelist;
896 }
897 return NULL;
898 }
900 filelist_entry_t *
901 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, struct mpd_song *song)
902 {
903 GList *list = g_list_first(fl->list);
905 while( list && song)
906 {
907 filelist_entry_t *entry = list->data;
908 mpd_InfoEntity *entity = entry->entity;
910 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
911 {
912 struct mpd_song *song2 = entity->info.song;
914 if( strcmp(song->file, song2->file) == 0 )
915 {
916 return entry;
917 }
918 }
919 list = list->next;
920 }
921 return NULL;
922 }
924 int
925 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
926 {
927 GList *list = g_list_first(fl->list);
929 if( fl->list==NULL || fl->length<1 )
930 return 0;
932 mpd_sendCommandListBegin(c->connection);
933 while( list )
934 {
935 filelist_entry_t *entry = list->data;
936 mpd_InfoEntity *entity = entry->entity;
938 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
939 {
940 struct mpd_song *song = entity->info.song;
942 mpd_sendAddCommand(c->connection, song->file);
943 }
944 list = list->next;
945 }
946 mpd_sendCommandListEnd(c->connection);
947 return mpdclient_finish_command(c);
948 }
957 GList *
958 mpdclient_get_artists_utf8(mpdclient_t *c)
959 {
960 gchar *str = NULL;
961 GList *list = NULL;
963 D("mpdclient_get_artists()\n");
964 mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
965 while( (str=mpd_getNextArtist(c->connection)) )
966 {
967 list = g_list_append(list, (gpointer) str);
968 }
969 if( mpdclient_finish_command(c) )
970 {
971 return string_list_free(list);
972 }
974 return list;
975 }
977 GList *
978 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
979 {
980 gchar *str = NULL;
981 GList *list = NULL;
983 D("mpdclient_get_albums(%s)\n", artist_utf8);
984 mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
985 while( (str=mpd_getNextAlbum(c->connection)) )
986 {
987 list = g_list_append(list, (gpointer) str);
988 }
989 if( mpdclient_finish_command(c) )
990 {
991 return string_list_free(list);
992 }
994 return list;
995 }