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"
33 #include "strfsong.h"
35 #undef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
36 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
37 #define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
38 #define ENABLE_SONG_ID
39 #define ENABLE_PLCHANGES
41 #define BUFSIZE 1024
43 #define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error)
45 /* from utils.c */
46 extern GList *string_list_free(GList *string_list);
49 /* filelist sorting functions */
50 static gint
51 compare_filelistentry_dir(gconstpointer filelist_entry1, gconstpointer filelist_entry2)
52 {
53 mpd_InfoEntity *e1, *e2;
54 char *key1, *key2;
55 int n = 0;
57 e1 = ((filelist_entry_t *)filelist_entry1)->entity;
58 e2 = ((filelist_entry_t *)filelist_entry2)->entity;
59 if (e1 && e2 &&
60 e1->type == MPD_INFO_ENTITY_TYPE_DIRECTORY &&
61 e2->type == MPD_INFO_ENTITY_TYPE_DIRECTORY)
62 {
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 }
69 return n;
70 }
72 /* sort by list-format */
73 gint
74 compare_filelistentry_format(gconstpointer filelist_entry1, gconstpointer filelist_entry2)
75 {
76 mpd_InfoEntity *e1, *e2;
77 char key1[BUFSIZE], key2[BUFSIZE];
78 int n = 0;
80 e1 = ((filelist_entry_t *)filelist_entry1)->entity;
81 e2 = ((filelist_entry_t *)filelist_entry2)->entity;
82 if (e1 && e2 &&
83 e1->type == MPD_INFO_ENTITY_TYPE_SONG &&
84 e2->type == MPD_INFO_ENTITY_TYPE_SONG)
85 {
86 strfsong(key1, BUFSIZE, LIST_FORMAT, e1->info.song);
87 strfsong(key2, BUFSIZE, LIST_FORMAT, e2->info.song);
88 n = strcmp(key1,key2);
89 }
90 return n;
91 }
94 /* Error callbacks */
95 static gint
96 error_cb(mpdclient_t *c, gint error, gchar *msg)
97 {
98 GList *list = c->error_callbacks;
100 if( list==NULL )
101 fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
103 while(list)
104 {
105 mpdc_error_cb_t cb = list->data;
106 if( cb )
107 cb(c, error, msg);
108 list=list->next;
109 }
110 mpd_clearError(c->connection);
111 return error;
112 }
114 #ifdef DEBUG
115 // Unused ath the moment
116 /*
117 #include "strfsong.h"
119 static gchar *
120 get_song_name(mpd_Song *song)
121 {
122 static gchar name[256];
124 strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
125 return name;
126 }
127 */
128 #endif
130 /****************************************************************************/
131 /*** mpdclient functions ****************************************************/
132 /****************************************************************************/
134 gint
135 mpdclient_finish_command(mpdclient_t *c)
136 {
137 mpd_finishCommand(c->connection);
139 if( c->connection->error )
140 {
141 gchar *msg = locale_to_utf8(c->connection->errorStr);
142 gint error = c->connection->error;
143 if( error == MPD_ERROR_ACK )
144 error = error | (c->connection->errorCode << 8);
145 if( c->connection->errorCode == MPD_ACK_ERROR_PERMISSION )
146 {
147 if(screen_auth(c) == 0) return 0;
148 }
149 error_cb(c, error, msg);
150 g_free(msg);
151 return error;
152 }
154 return 0;
155 }
157 mpdclient_t *
158 mpdclient_new(void)
159 {
160 mpdclient_t *c;
162 c = g_malloc0(sizeof(mpdclient_t));
164 return c;
165 }
167 mpdclient_t *
168 mpdclient_free(mpdclient_t *c)
169 {
170 mpdclient_disconnect(c);
171 g_list_free(c->error_callbacks);
172 g_list_free(c->playlist_callbacks);
173 g_list_free(c->browse_callbacks);
174 g_free(c);
176 return NULL;
177 }
179 gint
180 mpdclient_disconnect(mpdclient_t *c)
181 {
182 if( c->connection )
183 mpd_closeConnection(c->connection);
184 c->connection = NULL;
186 if( c->status )
187 mpd_freeStatus(c->status);
188 c->status = NULL;
190 if( c->playlist.list )
191 mpdclient_playlist_free(&c->playlist);
193 if( c->song )
194 c->song = NULL;
196 return 0;
197 }
199 gint
200 mpdclient_connect(mpdclient_t *c,
201 gchar *host,
202 gint port,
203 gfloat timeout,
204 gchar *password)
205 {
206 gint retval = 0;
208 /* close any open connection */
209 if( c->connection )
210 mpdclient_disconnect(c);
212 /* connect to MPD */
213 c->connection = mpd_newConnection(host, port, timeout);
214 if( c->connection->error )
215 return error_cb(c, c->connection->error, c->connection->errorStr);
217 /* send password */
218 if( password )
219 {
220 mpd_sendPasswordCommand(c->connection, password);
221 retval = mpdclient_finish_command(c);
222 }
223 c->need_update = TRUE;
225 return retval;
226 }
228 gint
229 mpdclient_update(mpdclient_t *c)
230 {
231 gint retval = 0;
233 if( MPD_ERROR(c) )
234 return -1;
236 /* free the old status */
237 if( c->status )
238 mpd_freeStatus(c->status);
240 /* retreive new status */
241 mpd_sendStatusCommand(c->connection);
242 c->status = mpd_getStatus(c->connection);
243 if( (retval=mpdclient_finish_command(c)) )
244 return retval;
245 #ifdef DEBUG
246 if( c->status->error )
247 D("status> %s\n", c->status->error);
248 #endif
250 /* check if the playlist needs an update */
251 if( c->playlist.id != c->status->playlist )
252 {
253 if( c->playlist.list )
254 retval = mpdclient_playlist_update_changes(c);
255 else
256 retval = mpdclient_playlist_update(c);
257 }
259 /* update the current song */
260 if( !c->song || c->status->songid != c->song->id )
261 {
262 c->song = playlist_get_song(c, c->status->song);
263 }
265 c->need_update = FALSE;
267 return retval;
268 }
271 /****************************************************************************/
272 /*** MPD Commands **********************************************************/
273 /****************************************************************************/
275 gint
276 mpdclient_cmd_play(mpdclient_t *c, gint index)
277 {
278 #ifdef ENABLE_SONG_ID
279 mpd_Song *song = playlist_get_song(c, index);
281 D("Play id:%d\n", song ? song->id : -1);
282 if( song )
283 mpd_sendPlayIdCommand(c->connection, song->id);
284 else
285 mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING);
286 #else
287 mpd_sendPlayCommand(c->connection, index);
288 #endif
289 c->need_update = TRUE;
290 return mpdclient_finish_command(c);
291 }
293 gint
294 mpdclient_cmd_pause(mpdclient_t *c, gint value)
295 {
296 mpd_sendPauseCommand(c->connection, value);
297 return mpdclient_finish_command(c);
298 }
300 gint
301 mpdclient_cmd_stop(mpdclient_t *c)
302 {
303 mpd_sendStopCommand(c->connection);
304 return mpdclient_finish_command(c);
305 }
307 gint
308 mpdclient_cmd_next(mpdclient_t *c)
309 {
310 mpd_sendNextCommand(c->connection);
311 c->need_update = TRUE;
312 return mpdclient_finish_command(c);
313 }
315 gint
316 mpdclient_cmd_prev(mpdclient_t *c)
317 {
318 mpd_sendPrevCommand(c->connection);
319 c->need_update = TRUE;
320 return mpdclient_finish_command(c);
321 }
323 gint
324 mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos)
325 {
326 D("Seek id:%d\n", id);
327 mpd_sendSeekIdCommand(c->connection, id, pos);
328 return mpdclient_finish_command(c);
329 }
331 gint
332 mpdclient_cmd_shuffle(mpdclient_t *c)
333 {
334 mpd_sendShuffleCommand(c->connection);
335 c->need_update = TRUE;
336 return mpdclient_finish_command(c);
337 }
339 gint
340 mpdclient_cmd_clear(mpdclient_t *c)
341 {
342 gint retval = 0;
344 mpd_sendClearCommand(c->connection);
345 retval = mpdclient_finish_command(c);
346 /* call playlist updated callback */
347 mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
348 c->need_update = TRUE;
349 return retval;
350 }
352 gint
353 mpdclient_cmd_repeat(mpdclient_t *c, gint value)
354 {
355 mpd_sendRepeatCommand(c->connection, value);
356 return mpdclient_finish_command(c);
357 }
359 gint
360 mpdclient_cmd_random(mpdclient_t *c, gint value)
361 {
362 mpd_sendRandomCommand(c->connection, value);
363 return mpdclient_finish_command(c);
364 }
366 gint
367 mpdclient_cmd_crossfade(mpdclient_t *c, gint value)
368 {
369 mpd_sendCrossfadeCommand(c->connection, value);
370 return mpdclient_finish_command(c);
371 }
373 gint
374 mpdclient_cmd_db_update_utf8(mpdclient_t *c, gchar *path)
375 {
376 mpd_sendUpdateCommand(c->connection, path ? path : "");
377 return mpdclient_finish_command(c);
378 }
380 gint
381 mpdclient_cmd_volume(mpdclient_t *c, gint value)
382 {
383 mpd_sendSetvolCommand(c->connection, value);
384 return mpdclient_finish_command(c);
385 }
387 gint
388 mpdclient_cmd_add_path_utf8(mpdclient_t *c, gchar *path_utf8)
389 {
390 mpd_sendAddCommand(c->connection, path_utf8);
391 return mpdclient_finish_command(c);
392 }
394 gint
395 mpdclient_cmd_add_path(mpdclient_t *c, gchar *path)
396 {
397 gint retval;
398 gchar *path_utf8 = locale_to_utf8(path);
400 retval=mpdclient_cmd_add_path_utf8(c, path_utf8);
401 g_free(path_utf8);
402 return retval;
403 }
405 gint
406 mpdclient_cmd_add(mpdclient_t *c, mpd_Song *song)
407 {
408 gint retval = 0;
410 if( !song || !song->file )
411 return -1;
413 /* send the add command to mpd */
414 mpd_sendAddCommand(c->connection, song->file);
415 if( (retval=mpdclient_finish_command(c)) )
416 return retval;
418 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
419 /* add the song to playlist */
420 c->playlist.list = g_list_append(c->playlist.list, mpd_songDup(song));
421 c->playlist.length++;
423 /* increment the playlist id, so we dont retrives a new playlist */
424 c->playlist.id++;
426 /* call playlist updated callback */
427 mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
428 #else
429 c->need_update = TRUE;
430 #endif
432 return 0;
433 }
435 gint
436 mpdclient_cmd_delete(mpdclient_t *c, gint index)
437 {
438 gint retval = 0;
439 mpd_Song *song = playlist_get_song(c, index);
441 if( !song )
442 return -1;
444 /* send the delete command to mpd */
445 #ifdef ENABLE_SONG_ID
446 D("Delete id:%d\n", song->id);
447 mpd_sendDeleteIdCommand(c->connection, song->id);
448 #else
449 mpd_sendDeleteCommand(c->connection, index);
450 #endif
451 if( (retval=mpdclient_finish_command(c)) )
452 return retval;
454 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
455 /* increment the playlist id, so we dont retrive a new playlist */
456 c->playlist.id++;
458 /* remove the song from the playlist */
459 c->playlist.list = g_list_remove(c->playlist.list, (gpointer) song);
460 c->playlist.length = g_list_length(c->playlist.list);
462 /* call playlist updated callback */
463 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
465 /* remove references to the song */
466 if( c->song == song )
467 {
468 c->song = NULL;
469 c->need_update = TRUE;
470 }
472 /* free song */
473 mpd_freeSong(song);
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, index1, index2;
486 GList *item1, *item2;
487 gpointer data1, data2;
488 mpd_Song *song1, *song2;
490 if( old_index==new_index || new_index<0 || new_index>=c->playlist.length )
491 return -1;
493 song1 = playlist_get_song(c, old_index);
494 song2 = playlist_get_song(c, new_index);
496 /* send the move command to mpd */
497 #ifdef ENABLE_SONG_ID
498 D("Swapping id:%d with id:%d\n", song1->id, song2->id);
499 mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
500 #else
501 D("Moving index %d to id:%d\n", old_index, new_index);
502 mpd_sendMoveCommand(c->connection, old_index, new_index);
503 #endif
504 if( (n=mpdclient_finish_command(c)) )
505 return n;
507 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
508 /* update the songs position field */
509 n = song1->pos;
510 song1->pos = song2->pos;
511 song2->pos = n;
512 index1 = MIN(old_index, new_index);
513 index2 = MAX(old_index, new_index);
514 /* retreive the list items */
515 item1 = g_list_nth(c->playlist.list, index1);
516 item2 = g_list_nth(c->playlist.list, index2);
517 /* retrieve the songs */
518 data1 = item1->data;
519 data2 = item2->data;
521 /* move the second item */
522 c->playlist.list = g_list_remove(c->playlist.list, data2);
523 c->playlist.list = g_list_insert_before(c->playlist.list, item1, data2);
525 /* move the first item */
526 if( index2-index1 >1 )
527 {
528 item2 = g_list_nth(c->playlist.list, index2);
529 c->playlist.list = g_list_remove(c->playlist.list, data1);
530 c->playlist.list = g_list_insert_before(c->playlist.list, item2, data1);
531 }
533 /* increment the playlist id, so we dont retrives a new playlist */
534 c->playlist.id++;
536 #else
537 c->need_update = TRUE;
538 #endif
540 /* call playlist updated callback */
541 D("move> new_index=%d, old_index=%d\n", new_index, old_index);
542 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
544 return 0;
545 }
547 gint
548 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
549 {
550 gint retval = 0;
552 mpd_sendSaveCommand(c->connection, filename_utf8);
553 if( (retval=mpdclient_finish_command(c)) == 0 )
554 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
555 return retval;
556 }
558 gint
559 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
560 {
561 gint retval = 0;
562 gchar *filename_utf8 = locale_to_utf8(filename);
564 retval = mpdclient_cmd_save_playlist_utf8(c, filename);
565 g_free(filename_utf8);
566 return retval;
567 }
569 gint
570 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
571 {
572 mpd_sendLoadCommand(c->connection, filename_utf8);
573 c->need_update = TRUE;
574 return mpdclient_finish_command(c);
575 }
577 gint
578 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
579 {
580 gint retval = 0;
582 mpd_sendRmCommand(c->connection, filename_utf8);
583 if( (retval=mpdclient_finish_command(c)) == 0 )
584 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
585 return retval;
586 }
588 gint
589 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
590 {
591 gint retval = 0;
592 gchar *filename_utf8 = locale_to_utf8(filename);
594 retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
595 g_free(filename_utf8);
596 return retval;
597 }
600 /****************************************************************************/
601 /*** Callback managment functions *******************************************/
602 /****************************************************************************/
603 static void
604 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
605 {
606 while(list)
607 {
608 mpdc_list_cb_t fn = list->data;
610 fn(c, event, data);
611 list=list->next;
612 }
613 }
615 void
616 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
617 {
618 do_list_callbacks(c, c->playlist_callbacks, event, data);
619 }
621 void
622 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
623 {
624 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
625 }
627 void
628 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
629 {
630 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
631 }
633 void
634 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
635 {
636 do_list_callbacks(c, c->browse_callbacks, event, data);
637 }
640 void
641 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
642 {
643 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
644 }
646 void
647 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
648 {
649 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
650 }
652 void
653 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
654 {
655 c->error_callbacks = g_list_append(c->error_callbacks, cb);
656 }
658 void
659 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
660 {
661 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
662 }
664 /****************************************************************************/
665 /*** Playlist managment functions *******************************************/
666 /****************************************************************************/
668 gint
669 mpdclient_playlist_free(mpdclient_playlist_t *playlist)
670 {
671 GList *list = g_list_first(playlist->list);
673 while(list)
674 {
675 mpd_Song *song = (mpd_Song *) list->data;
676 mpd_freeSong(song);
677 list=list->next;
678 }
679 g_list_free(playlist->list);
680 memset(playlist, 0, sizeof(mpdclient_playlist_t));
681 return 0;
682 }
684 /* update playlist */
685 gint
686 mpdclient_playlist_update(mpdclient_t *c)
687 {
688 mpd_InfoEntity *entity;
690 D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
692 if( MPD_ERROR(c) )
693 return -1;
695 if( c->playlist.list )
696 mpdclient_playlist_free(&c->playlist);
698 mpd_sendPlaylistInfoCommand(c->connection,-1);
699 while( (entity=mpd_getNextInfoEntity(c->connection)) )
700 {
701 if(entity->type==MPD_INFO_ENTITY_TYPE_SONG)
702 {
703 mpd_Song *song = mpd_songDup(entity->info.song);
705 c->playlist.list = g_list_append(c->playlist.list, (gpointer) song);
706 c->playlist.length++;
707 }
708 mpd_freeInfoEntity(entity);
709 }
710 c->playlist.id = c->status->playlist;
711 c->song = NULL;
712 c->playlist.updated = TRUE;
714 /* call playlist updated callbacks */
715 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
717 return mpdclient_finish_command(c);
718 }
720 #ifdef ENABLE_PLCHANGES
722 gint
723 mpdclient_compare_songs(gconstpointer a, gconstpointer b)
724 {
725 mpd_Song *song1 = (mpd_Song *) a;
726 mpd_Song *song2 = (mpd_Song *) b;
728 return song1->pos - song2->pos;
729 }
733 /* update playlist (plchanges) */
734 gint
735 mpdclient_playlist_update_changes(mpdclient_t *c)
736 {
737 mpd_InfoEntity *entity;
739 D("mpdclient_playlist_update_changes() [%lld -> %lld]\n",
740 c->status->playlist, c->playlist.id);
742 if( MPD_ERROR(c) )
743 return -1;
745 mpd_sendPlChangesCommand(c->connection, c->playlist.id);
747 while( (entity=mpd_getNextInfoEntity(c->connection)) != NULL )
748 {
749 mpd_Song *song = entity->info.song;
751 if( song->pos < c->playlist.length )
752 {
753 GList *item = g_list_nth(c->playlist.list, song->pos);
755 /* update song */
756 D("updating pos:%d, id=%d [%p] - %s\n",
757 song->pos, song->id, item, song->file);
758 mpd_freeSong((mpd_Song *) item->data);
759 item->data = mpd_songDup(song);
760 }
761 else
762 {
763 /* add a new song */
764 D("adding song at pos %d\n", song->pos);
765 c->playlist.list = g_list_append(c->playlist.list,
766 (gpointer) mpd_songDup(song));
767 }
769 }
771 /* remove trailing songs */
772 while( c->status->playlistLength < c->playlist.length )
773 {
774 GList *item = g_list_last(c->playlist.list);
776 /* Remove the last playlist entry */
777 D("removing song at pos %d\n", ((mpd_Song *) item->data)->pos);
778 mpd_freeSong((mpd_Song *) item->data);
779 c->playlist.list = g_list_delete_link(c->playlist.list, item);
780 c->playlist.length = g_list_length(c->playlist.list);
781 }
783 c->song = NULL;
784 c->playlist.id = c->status->playlist;
785 c->playlist.updated = TRUE;
786 c->playlist.length = g_list_length(c->playlist.list);
788 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
790 return 0;
791 }
793 #else
794 gint
795 mpdclient_playlist_update_changes(mpdclient_t *c)
796 {
797 return mpdclient_playlist_update(c);
798 }
799 #endif
801 mpd_Song *
802 playlist_get_song(mpdclient_t *c, gint index)
803 {
804 return (mpd_Song *) g_list_nth_data(c->playlist.list, index);
805 }
807 GList *
808 playlist_lookup(mpdclient_t *c, int id)
809 {
810 GList *list = g_list_first(c->playlist.list);
812 while( list )
813 {
814 mpd_Song *song = (mpd_Song *) list->data;
815 if( song->id == id )
816 return list;
817 list=list->next;
818 }
819 return NULL;
820 }
822 mpd_Song *
823 playlist_lookup_song(mpdclient_t *c, gint id)
824 {
825 GList *list = c->playlist.list;
827 while( list )
828 {
829 mpd_Song *song = (mpd_Song *) list->data;
830 if( song->id == id )
831 return song;
832 list=list->next;
833 }
834 return NULL;
835 }
837 gint
838 playlist_get_index(mpdclient_t *c, mpd_Song *song)
839 {
840 return g_list_index(c->playlist.list, song);
841 }
843 gint
844 playlist_get_index_from_id(mpdclient_t *c, gint id)
845 {
846 return g_list_index(c->playlist.list, playlist_lookup_song(c, id));
847 }
849 gint
850 playlist_get_index_from_file(mpdclient_t *c, gchar *filename)
851 {
852 GList *list = c->playlist.list;
853 gint i=0;
855 while( list )
856 {
857 mpd_Song *song = (mpd_Song *) list->data;
858 if( strcmp(song->file, filename ) == 0 )
859 return i;
860 list=list->next;
861 i++;
862 }
863 return -1;
864 }
867 /****************************************************************************/
868 /*** Filelist functions *****************************************************/
869 /****************************************************************************/
871 mpdclient_filelist_t *
872 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
873 {
874 GList *list = g_list_first(filelist->list);
876 D("mpdclient_filelist_free()\n");
877 if( list == NULL )
878 return NULL;
879 while( list!=NULL )
880 {
881 filelist_entry_t *entry = list->data;
883 if( entry->entity )
884 mpd_freeInfoEntity(entry->entity);
885 g_free(entry);
886 list=list->next;
887 }
888 g_list_free(filelist->list);
889 g_free(filelist->path);
890 filelist->path = NULL;
891 filelist->list = NULL;
892 filelist->length = 0;
893 g_free(filelist);
895 return NULL;
896 }
899 mpdclient_filelist_t *
900 mpdclient_filelist_get(mpdclient_t *c, gchar *path)
901 {
902 mpdclient_filelist_t *filelist;
903 mpd_InfoEntity *entity;
904 gchar *path_utf8 = locale_to_utf8(path);
905 gboolean has_dirs_only = TRUE;
907 D("mpdclient_filelist_get(%s)\n", path);
908 mpd_sendLsInfoCommand(c->connection, path_utf8);
909 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
910 if( path && path[0] && strcmp(path, "/") )
911 {
912 /* add a dummy entry for ./.. */
913 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
914 entry->entity = NULL;
915 filelist->list = g_list_append(filelist->list, (gpointer) entry);
916 filelist->length++;
917 }
919 while( (entity=mpd_getNextInfoEntity(c->connection)) )
920 {
921 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
923 entry->entity = entity;
924 filelist->list = g_list_append(filelist->list, (gpointer) entry);
925 filelist->length++;
927 if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY)
928 {
929 has_dirs_only = FALSE;
930 }
931 }
933 /* If there's an error, ignore it. We'll return an empty filelist. */
934 mpdclient_finish_command(c);
936 g_free(path_utf8);
937 filelist->path = g_strdup(path);
938 filelist->updated = TRUE;
940 // If there are only directory entities in the filelist, we sort it
941 if (has_dirs_only)
942 {
943 D("mpdclient_filelist_get: only dirs; sorting!\n");
944 filelist->list = g_list_sort(filelist->list, compare_filelistentry_dir);
945 }
947 return filelist;
948 }
950 mpdclient_filelist_t *
951 mpdclient_filelist_search_utf8(mpdclient_t *c,
952 int exact_match,
953 int table,
954 gchar *filter_utf8)
955 {
956 mpdclient_filelist_t *filelist;
957 mpd_InfoEntity *entity;
959 D("mpdclient_filelist_search(%s)\n", filter_utf8);
960 if( exact_match )
961 mpd_sendFindCommand(c->connection, table, filter_utf8);
962 else
963 mpd_sendSearchCommand(c->connection, table, filter_utf8);
964 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
966 while( (entity=mpd_getNextInfoEntity(c->connection)) )
967 {
968 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
970 entry->entity = entity;
971 filelist->list = g_list_append(filelist->list, (gpointer) entry);
972 filelist->length++;
973 }
975 if( mpdclient_finish_command(c) )
976 return mpdclient_filelist_free(filelist);
978 filelist->updated = TRUE;
980 return filelist;
981 }
984 mpdclient_filelist_t *
985 mpdclient_filelist_search(mpdclient_t *c,
986 int exact_match,
987 int table,
988 gchar *filter)
989 {
990 mpdclient_filelist_t *filelist;
991 gchar *filter_utf8 = locale_to_utf8(filter);
993 D("mpdclient_filelist_search(%s)\n", filter);
994 filelist = mpdclient_filelist_search_utf8(c,exact_match,table,filter_utf8);
995 g_free(filter_utf8);
997 return filelist;
998 }
1000 mpdclient_filelist_t *
1001 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
1002 {
1003 if( filelist != NULL )
1004 {
1005 gchar *path = g_strdup(filelist->path);
1007 filelist = mpdclient_filelist_free(filelist);
1008 filelist = mpdclient_filelist_get(c, path);
1009 g_free(path);
1010 return filelist;
1011 }
1012 return NULL;
1013 }
1015 filelist_entry_t *
1016 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song)
1017 {
1018 GList *list = g_list_first(fl->list);
1020 while( list && song)
1021 {
1022 filelist_entry_t *entry = list->data;
1023 mpd_InfoEntity *entity = entry->entity;
1025 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
1026 {
1027 mpd_Song *song2 = entity->info.song;
1029 if( strcmp(song->file, song2->file) == 0 )
1030 {
1031 return entry;
1032 }
1033 }
1034 list = list->next;
1035 }
1036 return NULL;
1037 }
1039 int
1040 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
1041 {
1042 GList *list = g_list_first(fl->list);
1044 if( fl->list==NULL || fl->length<1 )
1045 return 0;
1047 mpd_sendCommandListBegin(c->connection);
1048 while( list )
1049 {
1050 filelist_entry_t *entry = list->data;
1051 mpd_InfoEntity *entity = entry->entity;
1053 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
1054 {
1055 mpd_Song *song = entity->info.song;
1057 mpd_sendAddCommand(c->connection, song->file);
1058 }
1059 list = list->next;
1060 }
1061 mpd_sendCommandListEnd(c->connection);
1062 return mpdclient_finish_command(c);
1063 }
1072 GList *
1073 mpdclient_get_artists_utf8(mpdclient_t *c)
1074 {
1075 gchar *str = NULL;
1076 GList *list = NULL;
1078 D("mpdclient_get_artists()\n");
1079 mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
1080 while( (str=mpd_getNextArtist(c->connection)) )
1081 {
1082 list = g_list_append(list, (gpointer) str);
1083 }
1084 if( mpdclient_finish_command(c) )
1085 {
1086 return string_list_free(list);
1087 }
1089 return list;
1090 }
1092 GList *
1093 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
1094 {
1095 gchar *str = NULL;
1096 GList *list = NULL;
1098 D("mpdclient_get_albums(%s)\n", artist_utf8);
1099 mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
1100 while( (str=mpd_getNextAlbum(c->connection)) )
1101 {
1102 list = g_list_append(list, (gpointer) str);
1103 }
1104 if( mpdclient_finish_command(c) )
1105 {
1106 return string_list_free(list);
1107 }
1109 return list;
1110 }