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 <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <time.h>
25 #include <string.h>
26 #include <glib.h>
28 #include "config.h"
29 #include "ncmpc.h"
30 #include "support.h"
31 #include "mpdclient.h"
32 #include "options.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
39 #define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)
42 /* Error callbacks */
43 static gint
44 error_cb(mpdclient_t *c, gint error, gchar *msg)
45 {
46 GList *list = c->error_callbacks;
48 if( list==NULL )
49 fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
51 while(list)
52 {
53 mpdc_error_cb_t cb = list->data;
54 if( cb )
55 cb(c, error, msg);
56 list=list->next;
57 }
58 mpd_clearError(c->connection);
59 return error;
60 }
62 #ifdef DEBUG
63 #include "strfsong.h"
65 static gchar *
66 get_song_name(mpd_Song *song)
67 {
68 static gchar name[256];
70 strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
71 return name;
72 }
74 #endif
76 /****************************************************************************/
77 /*** mpdclient functions ****************************************************/
78 /****************************************************************************/
80 gint
81 mpdclient_finish_command(mpdclient_t *c)
82 {
83 mpd_finishCommand(c->connection);
85 if( c->connection->error )
86 {
87 gchar *msg = locale_to_utf8(c->connection->errorStr);
88 gint error = c->connection->error;
90 if( error == MPD_ERROR_ACK )
91 error = error | (c->connection->errorCode << 8);
93 error_cb(c, error, msg);
94 g_free(msg);
95 return error;
96 }
98 return 0;
99 }
101 mpdclient_t *
102 mpdclient_new(void)
103 {
104 mpdclient_t *c;
106 c = g_malloc0(sizeof(mpdclient_t));
108 return c;
109 }
111 mpdclient_t *
112 mpdclient_free(mpdclient_t *c)
113 {
114 mpdclient_disconnect(c);
115 g_list_free(c->error_callbacks);
116 g_list_free(c->playlist_callbacks);
117 g_list_free(c->browse_callbacks);
118 g_free(c);
120 return NULL;
121 }
123 gint
124 mpdclient_disconnect(mpdclient_t *c)
125 {
126 if( c->connection )
127 mpd_closeConnection(c->connection);
128 c->connection = NULL;
130 if( c->status )
131 mpd_freeStatus(c->status);
132 c->status = NULL;
134 if( c->playlist.list )
135 mpdclient_playlist_free(&c->playlist);
137 if( c->song )
138 c->song = NULL;
140 return 0;
141 }
143 gint
144 mpdclient_connect(mpdclient_t *c,
145 gchar *host,
146 gint port,
147 gfloat timeout,
148 gchar *password)
149 {
150 gint retval = 0;
152 /* close any open connection */
153 if( c->connection )
154 mpdclient_disconnect(c);
156 /* connect to MPD */
157 c->connection = mpd_newConnection(host, port, timeout);
158 if( c->connection->error )
159 return error_cb(c, c->connection->error, c->connection->errorStr);
161 /* send password */
162 if( password )
163 {
164 mpd_sendPasswordCommand(c->connection, password);
165 retval = mpdclient_finish_command(c);
166 }
167 c->need_update = TRUE;
169 return retval;
170 }
172 gint
173 mpdclient_update(mpdclient_t *c)
174 {
175 gint retval = 0;
177 if( MPD_ERROR(c) )
178 return -1;
180 /* free the old status */
181 if( c->status )
182 mpd_freeStatus(c->status);
184 /* retreive new status */
185 mpd_sendStatusCommand(c->connection);
186 c->status = mpd_getStatus(c->connection);
187 if( (retval=mpdclient_finish_command(c)) )
188 return retval;
189 #ifdef DEBUG
190 if( c->status->error )
191 D("status> %s\n", c->status->error);
192 #endif
194 /* check if the playlist needs an update */
195 if( c->playlist.id != c->status->playlist )
196 {
197 if( c->playlist.list )
198 retval = mpdclient_playlist_update_changes(c);
199 else
200 retval = mpdclient_playlist_update(c);
201 }
203 /* update the current song */
204 if( !c->song || c->status->songid != c->song->id )
205 {
206 c->song = playlist_get_song(c, c->status->song);
207 }
209 c->need_update = FALSE;
211 return retval;
212 }
215 /****************************************************************************/
216 /*** MPD Commands **********************************************************/
217 /****************************************************************************/
219 gint
220 mpdclient_cmd_play(mpdclient_t *c, gint index)
221 {
222 #ifdef ENABLE_SONG_ID
223 mpd_Song *song = playlist_get_song(c, index);
225 D("Play id:%d\n", song ? song->id : -1);
226 if( song )
227 mpd_sendPlayIdCommand(c->connection, song->id);
228 else
229 mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
230 #else
231 mpd_sendPlayCommand(c->connection, index);
232 #endif
233 c->need_update = TRUE;
234 return mpdclient_finish_command(c);
235 }
237 gint
238 mpdclient_cmd_pause(mpdclient_t *c, gint value)
239 {
240 mpd_sendPauseCommand(c->connection, value);
241 return mpdclient_finish_command(c);
242 }
244 gint
245 mpdclient_cmd_stop(mpdclient_t *c)
246 {
247 mpd_sendStopCommand(c->connection);
248 return mpdclient_finish_command(c);
249 }
251 gint
252 mpdclient_cmd_next(mpdclient_t *c)
253 {
254 mpd_sendNextCommand(c->connection);
255 c->need_update = TRUE;
256 return mpdclient_finish_command(c);
257 }
259 gint
260 mpdclient_cmd_prev(mpdclient_t *c)
261 {
262 mpd_sendPrevCommand(c->connection);
263 c->need_update = TRUE;
264 return mpdclient_finish_command(c);
265 }
267 gint
268 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
269 {
270 D("Seek id:%d\n", id);
271 mpd_sendSeekIdCommand(c->connection, id, pos);
272 return mpdclient_finish_command(c);
273 }
275 gint
276 mpdclient_cmd_shuffle(mpdclient_t *c)
277 {
278 mpd_sendShuffleCommand(c->connection);
279 c->need_update = TRUE;
280 return mpdclient_finish_command(c);
281 }
283 gint
284 mpdclient_cmd_clear(mpdclient_t *c)
285 {
286 gint retval = 0;
288 mpd_sendClearCommand(c->connection);
289 retval = mpdclient_finish_command(c);
290 /* call playlist updated callback */
291 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
292 c->need_update = TRUE;
293 return retval;
294 }
296 gint
297 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
298 {
299 mpd_sendRepeatCommand(c->connection, value);
300 return mpdclient_finish_command(c);
301 }
303 gint
304 mpdclient_cmd_random(mpdclient_t *c, gint value)
305 {
306 mpd_sendRandomCommand(c->connection, value);
307 return mpdclient_finish_command(c);
308 }
310 gint
311 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
312 {
313 mpd_sendCrossfadeCommand(c->connection, value);
314 return mpdclient_finish_command(c);
315 }
317 gint
318 mpdclient_cmd_db_update(mpdclient_t *c)
319 {
320 mpd_sendUpdateCommand(c->connection);
321 return mpdclient_finish_command(c);
322 }
324 gint
325 mpdclient_cmd_volume(mpdclient_t *c, gint value)
326 {
327 mpd_sendSetvolCommand(c->connection, value);
328 return mpdclient_finish_command(c);
329 }
331 gint
332 mpdclient_cmd_add(mpdclient_t *c, mpd_Song *song)
333 {
334 gint retval = 0;
336 if( !song || !song->file )
337 return -1;
339 /* send the add command to mpd */
340 mpd_sendAddCommand(c->connection, song->file);
341 if( (retval=mpdclient_finish_command(c)) )
342 return retval;
344 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
345 /* add the song to playlist */
346 c->playlist.list = g_list_append(c->playlist.list, mpd_songDup(song));
347 c->playlist.length++;
349 /* increment the playlist id, so we dont retrives a new playlist */
350 c->playlist.id++;
352 /* call playlist updated callback */
353 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
354 #else
355 c->need_update = TRUE;
356 #endif
358 return 0;
359 }
361 gint
362 mpdclient_cmd_delete(mpdclient_t *c, gint index)
363 {
364 gint retval = 0;
365 mpd_Song *song = playlist_get_song(c, index);
367 if( !song )
368 return -1;
370 /* send the delete command to mpd */
371 #ifdef ENABLE_SONG_ID
372 D("Delete id:%d\n", song->id);
373 mpd_sendDeleteIdCommand(c->connection, song->id);
374 #else
375 mpd_sendDeleteCommand(c->connection, index);
376 #endif
377 if( (retval=mpdclient_finish_command(c)) )
378 return retval;
380 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
381 /* increment the playlist id, so we dont retrive a new playlist */
382 c->playlist.id++;
384 /* remove the song from the playlist */
385 c->playlist.list = g_list_remove(c->playlist.list, (gpointer) song);
386 c->playlist.length = g_list_length(c->playlist.list);
388 /* call playlist updated callback */
389 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
391 /* remove references to the song */
392 if( c->song == song )
393 {
394 c->song = NULL;
395 c->need_update = TRUE;
396 }
398 /* free song */
399 mpd_freeSong(song);
401 #else
402 c->need_update = TRUE;
403 #endif
405 return 0;
406 }
408 gint
409 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
410 {
411 gint n, index1, index2;
412 GList *item1, *item2;
413 gpointer data1, data2;
414 mpd_Song *song1, *song2;
416 if( old_index==new_index || new_index<0 || new_index>=c->playlist.length )
417 return -1;
419 song1 = playlist_get_song(c, old_index);
420 song2 = playlist_get_song(c, new_index);
422 /* send the move command to mpd */
423 #ifdef ENABLE_SONG_ID
424 D("Swaping id:%d with id:%d\n", song1->id, song2->id);
425 mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
426 #else
427 D("Moving index %d to id:%d\n", old_index, new_index);
428 mpd_sendMoveCommand(c->connection, old_index, new_index);
429 #endif
430 if( (n=mpdclient_finish_command(c)) )
431 return n;
433 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
434 /* update the songs position field */
435 n = song1->pos;
436 song1->pos = song2->pos;
437 song2->pos = n;
438 index1 = MIN(old_index, new_index);
439 index2 = MAX(old_index, new_index);
440 /* retreive the list items */
441 item1 = g_list_nth(c->playlist.list, index1);
442 item2 = g_list_nth(c->playlist.list, index2);
443 /* retrieve the songs */
444 data1 = item1->data;
445 data2 = item2->data;
447 /* move the second item */
448 c->playlist.list = g_list_remove(c->playlist.list, data2);
449 c->playlist.list = g_list_insert_before(c->playlist.list, item1, data2);
451 /* move the first item */
452 if( index2-index1 >1 )
453 {
454 item2 = g_list_nth(c->playlist.list, index2);
455 c->playlist.list = g_list_remove(c->playlist.list, data1);
456 c->playlist.list = g_list_insert_before(c->playlist.list, item2, data1);
457 }
459 /* increment the playlist id, so we dont retrives a new playlist */
460 c->playlist.id++;
462 #else
463 c->need_update = TRUE;
464 #endif
466 /* call playlist updated callback */
467 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
469 return 0;
470 }
472 gint
473 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
474 {
475 gint retval = 0;
476 gchar *filename_utf8 = locale_to_utf8(filename);
478 mpd_sendSaveCommand(c->connection, filename_utf8);
479 if( (retval=mpdclient_finish_command(c)) == 0 )
480 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
481 g_free(filename_utf8);
482 return retval;
483 }
485 gint
486 mpdclient_cmd_load_playlist(mpdclient_t *c, gchar *filename_utf8)
487 {
488 mpd_sendLoadCommand(c->connection, filename_utf8);
489 c->need_update = TRUE;
490 return mpdclient_finish_command(c);
491 }
493 gint
494 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename_utf8)
495 {
496 gint retval = 0;
498 mpd_sendRmCommand(c->connection, filename_utf8);
499 if( (retval=mpdclient_finish_command(c)) == 0 )
500 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
501 return retval;
502 }
505 /****************************************************************************/
506 /*** Callback managment functions *******************************************/
507 /****************************************************************************/
508 static void
509 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
510 {
511 while(list)
512 {
513 mpdc_list_cb_t fn = list->data;
515 fn(c, event, data);
516 list=list->next;
517 }
518 }
520 void
521 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
522 {
523 do_list_callbacks(c, c->playlist_callbacks, event, data);
524 }
526 void
527 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
528 {
529 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
530 }
532 void
533 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
534 {
535 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
536 }
538 void
539 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
540 {
541 do_list_callbacks(c, c->browse_callbacks, event, data);
542 }
545 void
546 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
547 {
548 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
549 }
551 void
552 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
553 {
554 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
555 }
557 void
558 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
559 {
560 c->error_callbacks = g_list_append(c->error_callbacks, cb);
561 }
563 void
564 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
565 {
566 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
567 }
569 /****************************************************************************/
570 /*** Playlist managment functions *******************************************/
571 /****************************************************************************/
573 gint
574 mpdclient_playlist_free(mpdclient_playlist_t *playlist)
575 {
576 GList *list = g_list_first(playlist->list);
578 while(list)
579 {
580 mpd_Song *song = (mpd_Song *) list->data;
581 mpd_freeSong(song);
582 list=list->next;
583 }
584 g_list_free(playlist->list);
585 memset(playlist, 0, sizeof(mpdclient_playlist_t));
586 return 0;
587 }
589 /* update playlist */
590 gint
591 mpdclient_playlist_update(mpdclient_t *c)
592 {
593 mpd_InfoEntity *entity;
595 D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
597 if( MPD_ERROR(c) )
598 return -1;
600 if( c->playlist.list )
601 mpdclient_playlist_free(&c->playlist);
603 mpd_sendPlaylistInfoCommand(c->connection,-1);
604 while( (entity=mpd_getNextInfoEntity(c->connection)) )
605 {
606 if(entity->type==MPD_INFO_ENTITY_TYPE_SONG)
607 {
608 mpd_Song *song = mpd_songDup(entity->info.song);
610 c->playlist.list = g_list_append(c->playlist.list, (gpointer) song);
611 c->playlist.length++;
612 }
613 mpd_freeInfoEntity(entity);
614 }
615 c->playlist.id = c->status->playlist;
616 c->song = NULL;
617 c->playlist.updated = TRUE;
619 /* call playlist updated callbacks */
620 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
622 return mpdclient_finish_command(c);
623 }
625 static gint
626 compare_songs(gconstpointer a, gconstpointer b)
627 {
628 mpd_Song *song1 = (mpd_Song *) a;
629 mpd_Song *song2 = (mpd_Song *) b;
631 return song1->pos - song2->pos;
632 }
634 /* update playlist (plchanges) */
635 gint
636 mpdclient_playlist_update_changes(mpdclient_t *c)
637 {
638 gboolean sort = FALSE;
639 mpd_InfoEntity *entity;
641 D("mpdclient_playlist_update_changes() [%lld -> %lld]\n",
642 c->status->playlist, c->playlist.id);
644 if( MPD_ERROR(c) )
645 return -1;
647 mpd_sendPlChangesCommand(c->connection, c->playlist.id);
649 while( (entity=mpd_getNextInfoEntity(c->connection)) != NULL )
650 {
651 if(entity->type==MPD_INFO_ENTITY_TYPE_SONG)
652 {
653 mpd_Song *song;
654 GList *item;
656 if( (song=mpd_songDup(entity->info.song)) == NULL )
657 {
658 D("song==NULL => calling mpdclient_playlist_update()\n");
659 return mpdclient_playlist_update(c);
660 }
662 item = playlist_lookup(c, song->id);
664 if( item && item->data)
665 {
666 /* Update playlist entry */
667 mpd_freeSong((mpd_Song *) item->data);
668 item->data = song;
669 if( c->song && c->song->id == song->id )
670 c->song = song;
671 if( !sort && g_list_position(c->playlist.list, item)!=song->pos )
672 sort = TRUE;
673 D("Changing index %d, num %d [%d] to %s\n",
674 g_list_position(c->playlist.list, item),
675 song->pos, song->id, get_song_name(song));
676 }
677 else
678 {
679 /* Add a new playlist entry */
680 D("Adding pos:%d, id;%d - %s\n",
681 song->pos, song->id, get_song_name(song));
682 c->playlist.list = g_list_append(c->playlist.list,
683 (gpointer) song);
684 c->playlist.length++;
685 }
686 }
687 mpd_freeInfoEntity(entity);
688 }
689 mpd_finishCommand(c->connection);
691 while( g_list_length(c->playlist.list) > c->status->playlistLength )
692 {
693 GList *item = g_list_last(c->playlist.list);
695 /* Remove the last playlist entry */
696 mpd_freeSong((mpd_Song *) item->data);
697 c->playlist.list = g_list_delete_link(c->playlist.list, item);
698 c->playlist.length--;
699 D("Removed the last playlist entry\n");
700 }
702 if( sort )
703 {
704 D("Sorting playlist...\n");
705 c->playlist.list = g_list_sort(c->playlist.list, compare_songs );
706 }
708 c->playlist.id = c->status->playlist;
709 c->playlist.updated = TRUE;
711 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
713 return 0;
714 }
716 mpd_Song *
717 playlist_get_song(mpdclient_t *c, gint index)
718 {
719 return (mpd_Song *) g_list_nth_data(c->playlist.list, index);
720 }
722 GList *
723 playlist_lookup(mpdclient_t *c, gint id)
724 {
725 GList *list = c->playlist.list;
727 while( list )
728 {
729 mpd_Song *song = (mpd_Song *) list->data;
730 if( song->id == id )
731 return list;
732 list=list->next;
733 }
734 return NULL;
735 }
737 mpd_Song *
738 playlist_lookup_song(mpdclient_t *c, gint id)
739 {
740 GList *list = c->playlist.list;
742 while( list )
743 {
744 mpd_Song *song = (mpd_Song *) list->data;
745 if( song->id == id )
746 return song;
747 list=list->next;
748 }
749 return NULL;
750 }
752 gint
753 playlist_get_index(mpdclient_t *c, mpd_Song *song)
754 {
755 return g_list_index(c->playlist.list, song);
756 }
758 gint
759 playlist_get_index_from_id(mpdclient_t *c, gint id)
760 {
761 return g_list_index(c->playlist.list, playlist_lookup_song(c, id));
762 }
764 gint
765 playlist_get_index_from_file(mpdclient_t *c, gchar *filename)
766 {
767 GList *list = c->playlist.list;
768 gint i=0;
770 while( list )
771 {
772 mpd_Song *song = (mpd_Song *) list->data;
773 if( strcmp(song->file, filename ) == 0 )
774 return i;
775 list=list->next;
776 i++;
777 }
778 return -1;
779 }
782 /****************************************************************************/
783 /*** Filelist functions *****************************************************/
784 /****************************************************************************/
786 mpdclient_filelist_t *
787 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
788 {
789 GList *list = g_list_first(filelist->list);
791 D("mpdclient_filelist_free()\n");
792 while( list!=NULL )
793 {
794 filelist_entry_t *entry = list->data;
796 if( entry->entity )
797 mpd_freeInfoEntity(entry->entity);
798 g_free(entry);
799 list=list->next;
800 }
801 g_list_free(filelist->list);
802 g_free(filelist->path);
803 filelist->path = NULL;
804 filelist->list = NULL;
805 filelist->length = 0;
806 g_free(filelist);
808 return NULL;
809 }
812 mpdclient_filelist_t *
813 mpdclient_filelist_get(mpdclient_t *c, gchar *path)
814 {
815 mpdclient_filelist_t *filelist;
816 mpd_InfoEntity *entity;
817 gchar *path_utf8 = locale_to_utf8(path);
819 D("mpdclient_filelist_get(%s)\n", path);
820 mpd_sendLsInfoCommand(c->connection, path_utf8);
821 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
822 if( path && path[0] && strcmp(path, "/") )
823 {
824 /* add a dummy entry for ./.. */
825 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
826 entry->entity = NULL;
827 filelist->list = g_list_append(filelist->list, (gpointer) entry);
828 filelist->length++;
829 }
831 while( (entity=mpd_getNextInfoEntity(c->connection)) )
832 {
833 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
835 entry->entity = entity;
836 filelist->list = g_list_append(filelist->list, (gpointer) entry);
837 filelist->length++;
838 }
840 if( mpdclient_finish_command(c) )
841 {
842 g_free(path_utf8);
843 return mpdclient_filelist_free(filelist);
844 }
846 g_free(path_utf8);
847 filelist->path = g_strdup(path);
848 filelist->updated = TRUE;
850 return filelist;
851 }
853 mpdclient_filelist_t *
854 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
855 {
856 if( filelist != NULL )
857 {
858 gchar *path = g_strdup(filelist->path);
860 filelist = mpdclient_filelist_free(filelist);
861 filelist = mpdclient_filelist_get(c, path);
862 g_free(path);
863 return filelist;
864 }
865 return NULL;
866 }
868 filelist_entry_t *
869 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song)
870 {
871 GList *list = g_list_first(fl->list);
873 while( list && song)
874 {
875 filelist_entry_t *entry = list->data;
876 mpd_InfoEntity *entity = entry->entity;
878 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
879 {
880 mpd_Song *song2 = entity->info.song;
882 if( strcmp(song->file, song2->file) == 0 )
883 {
884 return entry;
885 }
886 }
887 list = list->next;
888 }
889 return NULL;
890 }