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 c->playlist.list = g_array_sized_new(FALSE, FALSE, sizeof(mpd_Song *), 1024);
168 return c;
169 }
171 mpdclient_t *
172 mpdclient_free(mpdclient_t *c)
173 {
174 mpdclient_disconnect(c);
175 g_list_free(c->error_callbacks);
176 g_list_free(c->playlist_callbacks);
177 g_list_free(c->browse_callbacks);
178 g_free(c);
180 return NULL;
181 }
183 gint
184 mpdclient_disconnect(mpdclient_t *c)
185 {
186 if( c->connection )
187 mpd_closeConnection(c->connection);
188 c->connection = NULL;
190 if( c->status )
191 mpd_freeStatus(c->status);
192 c->status = NULL;
194 if( c->playlist.list )
195 mpdclient_playlist_free(&c->playlist);
197 if( c->song )
198 c->song = NULL;
200 return 0;
201 }
203 gint
204 mpdclient_connect(mpdclient_t *c,
205 gchar *host,
206 gint port,
207 gfloat _timeout,
208 gchar *password)
209 {
210 gint retval = 0;
212 /* close any open connection */
213 if( c->connection )
214 mpdclient_disconnect(c);
216 /* connect to MPD */
217 c->connection = mpd_newConnection(host, port, _timeout);
218 if( c->connection->error )
219 return error_cb(c, c->connection->error,
220 c->connection->errorStr);
222 /* send password */
223 if( password ) {
224 mpd_sendPasswordCommand(c->connection, password);
225 retval = mpdclient_finish_command(c);
226 }
227 c->need_update = TRUE;
229 return retval;
230 }
232 gint
233 mpdclient_update(mpdclient_t *c)
234 {
235 gint retval = 0;
237 if( MPD_ERROR(c) )
238 return -1;
240 /* free the old status */
241 if( c->status )
242 mpd_freeStatus(c->status);
244 /* retreive new status */
245 mpd_sendStatusCommand(c->connection);
246 c->status = mpd_getStatus(c->connection);
247 if( (retval=mpdclient_finish_command(c)) )
248 return retval;
249 #ifndef NDEBUG
250 if( c->status->error )
251 D("status> %s\n", c->status->error);
252 #endif
254 /* check if the playlist needs an update */
255 if( c->playlist.id != c->status->playlist )
256 {
257 if( c->playlist.list )
258 retval = mpdclient_playlist_update_changes(c);
259 else
260 retval = mpdclient_playlist_update(c);
261 }
263 /* update the current song */
264 if( !c->song || c->status->songid != c->song->id )
265 {
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 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, 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 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 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, mpd_Song *, old_index) = song2;
515 g_array_index(c->playlist.list, 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 /****************************************************************************/
652 gint
653 mpdclient_playlist_free(mpdclient_playlist_t *playlist)
654 {
655 guint i;
657 for (i = 0; i < playlist->list->len; ++i) {
658 mpd_Song *song = g_array_index(playlist->list, mpd_Song *, i);
659 mpd_freeSong(song);
660 }
662 g_array_free(playlist->list, TRUE);
663 memset(playlist, 0, sizeof(mpdclient_playlist_t));
664 return 0;
665 }
667 /* update playlist */
668 gint
669 mpdclient_playlist_update(mpdclient_t *c)
670 {
671 mpd_InfoEntity *entity;
673 D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
675 if (MPD_ERROR(c))
676 return -1;
678 if (c->playlist.list)
679 mpdclient_playlist_free(&c->playlist);
681 mpd_sendPlaylistInfoCommand(c->connection,-1);
682 while ((entity = mpd_getNextInfoEntity(c->connection))) {
683 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
684 mpd_Song *song = mpd_songDup(entity->info.song);
685 g_array_append_val(c->playlist.list, song);
686 }
687 mpd_freeInfoEntity(entity);
688 }
690 c->playlist.id = c->status->playlist;
691 c->song = NULL;
692 c->playlist.updated = TRUE;
694 /* call playlist updated callbacks */
695 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
697 return mpdclient_finish_command(c);
698 }
700 #ifdef ENABLE_PLCHANGES
702 /* update playlist (plchanges) */
703 gint
704 mpdclient_playlist_update_changes(mpdclient_t *c)
705 {
706 mpd_InfoEntity *entity;
708 D("mpdclient_playlist_update_changes() [%lld -> %lld]\n",
709 c->status->playlist, c->playlist.id);
711 if (MPD_ERROR(c))
712 return -1;
714 mpd_sendPlChangesCommand(c->connection, c->playlist.id);
716 while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) {
717 mpd_Song *song = mpd_songDup(entity->info.song);
719 if (song->pos >= 0 && (guint)song->pos < c->playlist.list->len) {
720 /* update song */
721 D("updating pos:%d, id=%d - %s\n",
722 song->pos, song->id, song->file);
723 mpd_freeSong(g_array_index(c->playlist.list,
724 mpd_Song *, song->pos));
725 g_array_index(c->playlist.list,
726 mpd_Song *, song->pos) = song;
727 } else {
728 /* add a new song */
729 D("adding song at pos %d\n", song->pos);
730 g_array_append_val(c->playlist.list, song);
731 }
733 mpd_freeInfoEntity(entity);
734 }
736 /* remove trailing songs */
737 while ((guint)c->status->playlistLength < c->playlist.list->len) {
738 guint pos = c->playlist.list->len - 1;
739 mpd_Song *song = g_array_index(c->playlist.list, mpd_Song *, pos);
741 /* Remove the last playlist entry */
742 D("removing song at pos %d\n", pos);
743 mpd_freeSong(song);
744 g_array_remove_index(c->playlist.list, pos);
745 }
747 c->song = NULL;
748 c->playlist.id = c->status->playlist;
749 c->playlist.updated = TRUE;
751 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
753 return 0;
754 }
756 #else
757 gint
758 mpdclient_playlist_update_changes(mpdclient_t *c)
759 {
760 return mpdclient_playlist_update(c);
761 }
762 #endif
764 mpd_Song *
765 playlist_get_song(mpdclient_t *c, gint idx)
766 {
767 if (idx < 0 || (guint)idx >= c->playlist.list->len)
768 return NULL;
770 return g_array_index(c->playlist.list, mpd_Song *, idx);
771 }
773 mpd_Song *
774 playlist_lookup_song(mpdclient_t *c, gint id)
775 {
776 guint i;
778 for (i = 0; i < c->playlist.list->len; ++i) {
779 mpd_Song *song = g_array_index(c->playlist.list,
780 mpd_Song *, i);
781 if (song->id == id)
782 return song;
783 }
785 return NULL;
786 }
788 gint
789 playlist_get_index(mpdclient_t *c, mpd_Song *song)
790 {
791 guint i;
793 for (i = 0; i < c->playlist.list->len; ++i) {
794 if (g_array_index(c->playlist.list, mpd_Song *, i)
795 == song)
796 return (gint)i;
797 }
799 return -1;
800 }
802 gint
803 playlist_get_index_from_id(mpdclient_t *c, gint id)
804 {
805 guint i;
807 for (i = 0; i < c->playlist.list->len; ++i) {
808 mpd_Song *song = g_array_index(c->playlist.list,
809 mpd_Song *, i);
810 if (song->id == id)
811 return (gint)i;
812 }
814 return -1;
815 }
817 gint
818 playlist_get_index_from_file(mpdclient_t *c, gchar *filename)
819 {
820 guint i;
822 for (i = 0; i < c->playlist.list->len; ++i) {
823 mpd_Song *song = g_array_index(c->playlist.list,
824 mpd_Song *, i);
825 if(strcmp(song->file, filename) == 0)
826 return (gint)i;
827 }
829 return -1;
830 }
833 /****************************************************************************/
834 /*** Filelist functions *****************************************************/
835 /****************************************************************************/
837 mpdclient_filelist_t *
838 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
839 {
840 GList *list = g_list_first(filelist->list);
842 D("mpdclient_filelist_free()\n");
843 if( list == NULL )
844 return NULL;
845 while( list!=NULL )
846 {
847 filelist_entry_t *entry = list->data;
849 if( entry->entity )
850 mpd_freeInfoEntity(entry->entity);
851 g_free(entry);
852 list=list->next;
853 }
854 g_list_free(filelist->list);
855 g_free(filelist->path);
856 filelist->path = NULL;
857 filelist->list = NULL;
858 filelist->length = 0;
859 g_free(filelist);
861 return NULL;
862 }
865 mpdclient_filelist_t *
866 mpdclient_filelist_get(mpdclient_t *c, const gchar *path)
867 {
868 mpdclient_filelist_t *filelist;
869 mpd_InfoEntity *entity;
870 gchar *path_utf8 = locale_to_utf8(path);
871 gboolean has_dirs_only = TRUE;
873 D("mpdclient_filelist_get(%s)\n", path);
874 mpd_sendLsInfoCommand(c->connection, path_utf8);
875 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
876 if( path && path[0] && strcmp(path, "/") )
877 {
878 /* add a dummy entry for ./.. */
879 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
880 entry->entity = NULL;
881 filelist->list = g_list_append(filelist->list, (gpointer) entry);
882 filelist->length++;
883 }
885 while( (entity=mpd_getNextInfoEntity(c->connection)) )
886 {
887 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
889 entry->entity = entity;
890 filelist->list = g_list_append(filelist->list, (gpointer) entry);
891 filelist->length++;
893 if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY)
894 {
895 has_dirs_only = FALSE;
896 }
897 }
899 /* If there's an error, ignore it. We'll return an empty filelist. */
900 mpdclient_finish_command(c);
902 g_free(path_utf8);
903 filelist->path = g_strdup(path);
904 filelist->updated = TRUE;
906 // If there are only directory entities in the filelist, we sort it
907 if (has_dirs_only)
908 {
909 D("mpdclient_filelist_get: only dirs; sorting!\n");
910 filelist->list = g_list_sort(filelist->list, compare_filelistentry_dir);
911 }
913 return filelist;
914 }
916 mpdclient_filelist_t *
917 mpdclient_filelist_search_utf8(mpdclient_t *c,
918 int exact_match,
919 int table,
920 gchar *filter_utf8)
921 {
922 mpdclient_filelist_t *filelist;
923 mpd_InfoEntity *entity;
925 D("mpdclient_filelist_search(%s)\n", filter_utf8);
926 if( exact_match )
927 mpd_sendFindCommand(c->connection, table, filter_utf8);
928 else
929 mpd_sendSearchCommand(c->connection, table, filter_utf8);
930 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
932 while( (entity=mpd_getNextInfoEntity(c->connection)) )
933 {
934 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
936 entry->entity = entity;
937 filelist->list = g_list_append(filelist->list, (gpointer) entry);
938 filelist->length++;
939 }
941 if( mpdclient_finish_command(c) )
942 return mpdclient_filelist_free(filelist);
944 filelist->updated = TRUE;
946 return filelist;
947 }
950 mpdclient_filelist_t *
951 mpdclient_filelist_search(mpdclient_t *c,
952 int exact_match,
953 int table,
954 gchar *_filter)
955 {
956 mpdclient_filelist_t *filelist;
957 gchar *filter_utf8 = locale_to_utf8(_filter);
959 D("mpdclient_filelist_search(%s)\n", _filter);
960 filelist = mpdclient_filelist_search_utf8(c, exact_match, table,
961 filter_utf8);
962 g_free(filter_utf8);
964 return filelist;
965 }
967 mpdclient_filelist_t *
968 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
969 {
970 if( filelist != NULL )
971 {
972 gchar *path = g_strdup(filelist->path);
974 filelist = mpdclient_filelist_free(filelist);
975 filelist = mpdclient_filelist_get(c, path);
976 g_free(path);
977 return filelist;
978 }
979 return NULL;
980 }
982 filelist_entry_t *
983 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song)
984 {
985 GList *list = g_list_first(fl->list);
987 while( list && song)
988 {
989 filelist_entry_t *entry = list->data;
990 mpd_InfoEntity *entity = entry->entity;
992 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
993 {
994 mpd_Song *song2 = entity->info.song;
996 if( strcmp(song->file, song2->file) == 0 )
997 {
998 return entry;
999 }
1000 }
1001 list = list->next;
1002 }
1003 return NULL;
1004 }
1006 int
1007 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
1008 {
1009 GList *list = g_list_first(fl->list);
1011 if( fl->list==NULL || fl->length<1 )
1012 return 0;
1014 mpd_sendCommandListBegin(c->connection);
1015 while( list )
1016 {
1017 filelist_entry_t *entry = list->data;
1018 mpd_InfoEntity *entity = entry->entity;
1020 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
1021 {
1022 mpd_Song *song = entity->info.song;
1024 mpd_sendAddCommand(c->connection, song->file);
1025 }
1026 list = list->next;
1027 }
1028 mpd_sendCommandListEnd(c->connection);
1029 return mpdclient_finish_command(c);
1030 }
1039 GList *
1040 mpdclient_get_artists_utf8(mpdclient_t *c)
1041 {
1042 gchar *str = NULL;
1043 GList *list = NULL;
1045 D("mpdclient_get_artists()\n");
1046 mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
1047 while( (str=mpd_getNextArtist(c->connection)) )
1048 {
1049 list = g_list_append(list, (gpointer) str);
1050 }
1051 if( mpdclient_finish_command(c) )
1052 {
1053 return string_list_free(list);
1054 }
1056 return list;
1057 }
1059 GList *
1060 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
1061 {
1062 gchar *str = NULL;
1063 GList *list = NULL;
1065 D("mpdclient_get_albums(%s)\n", artist_utf8);
1066 mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
1067 while( (str=mpd_getNextAlbum(c->connection)) )
1068 {
1069 list = g_list_append(list, (gpointer) str);
1070 }
1071 if( mpdclient_finish_command(c) )
1072 {
1073 return string_list_free(list);
1074 }
1076 return list;
1077 }