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,
52 gconstpointer filelist_entry2)
53 {
54 mpd_InfoEntity *e1, *e2;
55 char *key1, *key2;
56 int n = 0;
58 e1 = ((filelist_entry_t *)filelist_entry1)->entity;
59 e2 = ((filelist_entry_t *)filelist_entry2)->entity;
61 if (e1 && e2 &&
62 e1->type == MPD_INFO_ENTITY_TYPE_DIRECTORY &&
63 e2->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) {
64 key1 = g_utf8_collate_key(e1->info.directory->path,-1);
65 key2 = g_utf8_collate_key(e2->info.directory->path,-1);
66 n = strcmp(key1,key2);
67 g_free(key1);
68 g_free(key2);
69 }
71 return n;
72 }
74 /* sort by list-format */
75 gint
76 compare_filelistentry_format(gconstpointer filelist_entry1,
77 gconstpointer filelist_entry2)
78 {
79 mpd_InfoEntity *e1, *e2;
80 char key1[BUFSIZE], key2[BUFSIZE];
81 int n = 0;
83 e1 = ((filelist_entry_t *)filelist_entry1)->entity;
84 e2 = ((filelist_entry_t *)filelist_entry2)->entity;
86 if (e1 && e2 &&
87 e1->type == MPD_INFO_ENTITY_TYPE_SONG &&
88 e2->type == MPD_INFO_ENTITY_TYPE_SONG) {
89 strfsong(key1, BUFSIZE, LIST_FORMAT, e1->info.song);
90 strfsong(key2, BUFSIZE, LIST_FORMAT, e2->info.song);
91 n = strcmp(key1,key2);
92 }
94 return n;
95 }
98 /* Error callbacks */
99 static gint
100 error_cb(mpdclient_t *c, gint error, gchar *msg)
101 {
102 GList *list = c->error_callbacks;
104 if( list==NULL )
105 fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg);
107 while(list)
108 {
109 mpdc_error_cb_t cb = list->data;
110 if( cb )
111 cb(c, error, msg);
112 list=list->next;
113 }
114 mpd_clearError(c->connection);
115 return error;
116 }
118 #ifndef NDEBUG
119 // Unused ath the moment
120 /*
121 #include "strfsong.h"
123 static gchar *
124 get_song_name(mpd_Song *song)
125 {
126 static gchar name[256];
128 strfsong(name, 256, "[%artist% - ]%title%|%file%", song);
129 return name;
130 }
131 */
132 #endif
134 /****************************************************************************/
135 /*** mpdclient functions ****************************************************/
136 /****************************************************************************/
138 gint
139 mpdclient_finish_command(mpdclient_t *c)
140 {
141 mpd_finishCommand(c->connection);
143 if( c->connection->error )
144 {
145 gchar *msg = locale_to_utf8(c->connection->errorStr);
146 gint error = c->connection->error;
147 if( error == MPD_ERROR_ACK )
148 error = error | (c->connection->errorCode << 8);
149 if( c->connection->errorCode == MPD_ACK_ERROR_PERMISSION )
150 {
151 if(screen_auth(c) == 0) return 0;
152 }
153 error_cb(c, error, msg);
154 g_free(msg);
155 return error;
156 }
158 return 0;
159 }
161 mpdclient_t *
162 mpdclient_new(void)
163 {
164 mpdclient_t *c;
166 c = g_malloc0(sizeof(mpdclient_t));
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 index)
281 {
282 #ifdef ENABLE_SONG_ID
283 mpd_Song *song = playlist_get_song(c, index);
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, index);
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 index)
441 {
442 gint retval = 0;
443 mpd_Song *song = playlist_get_song(c, index);
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, index);
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 c->playlist.list = g_list_remove(c->playlist.list, (gpointer) song);
464 c->playlist.length = g_list_length(c->playlist.list);
466 /* call playlist updated callback */
467 mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
469 /* remove references to the song */
470 if (c->song == song) {
471 c->song = NULL;
472 c->need_update = TRUE;
473 }
475 /* free song */
476 mpd_freeSong(song);
478 #else
479 c->need_update = TRUE;
480 #endif
482 return 0;
483 }
485 gint
486 mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index)
487 {
488 gint n, index1, index2;
489 GList *item1, *item2;
490 gpointer data1, data2;
491 mpd_Song *song1, *song2;
493 if (old_index == new_index || new_index < 0 ||
494 new_index >= c->playlist.length)
495 return -1;
497 song1 = playlist_get_song(c, old_index);
498 song2 = playlist_get_song(c, new_index);
500 /* send the move command to mpd */
501 #ifdef ENABLE_SONG_ID
502 D("Swapping id:%d with id:%d\n", song1->id, song2->id);
503 mpd_sendSwapIdCommand(c->connection, song1->id, song2->id);
504 #else
505 D("Moving index %d to id:%d\n", old_index, new_index);
506 mpd_sendMoveCommand(c->connection, old_index, new_index);
507 #endif
508 if( (n=mpdclient_finish_command(c)) )
509 return n;
511 #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
512 /* update the songs position field */
513 n = song1->pos;
514 song1->pos = song2->pos;
515 song2->pos = n;
516 index1 = MIN(old_index, new_index);
517 index2 = MAX(old_index, new_index);
518 /* retreive the list items */
519 item1 = g_list_nth(c->playlist.list, index1);
520 item2 = g_list_nth(c->playlist.list, index2);
521 /* retrieve the songs */
522 data1 = item1->data;
523 data2 = item2->data;
525 /* move the second item */
526 c->playlist.list = g_list_remove(c->playlist.list, data2);
527 c->playlist.list = g_list_insert_before(c->playlist.list, item1, data2);
529 /* move the first item */
530 if (index2-index1 > 1) {
531 item2 = g_list_nth(c->playlist.list, index2);
532 c->playlist.list = g_list_remove(c->playlist.list, data1);
533 c->playlist.list = g_list_insert_before(c->playlist.list,
534 item2, data1);
535 }
537 /* increment the playlist id, so we dont retrives a new playlist */
538 c->playlist.id++;
540 #else
541 c->need_update = TRUE;
542 #endif
544 /* call playlist updated callback */
545 D("move> new_index=%d, old_index=%d\n", new_index, old_index);
546 mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
548 return 0;
549 }
551 gint
552 mpdclient_cmd_save_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
553 {
554 gint retval = 0;
556 mpd_sendSaveCommand(c->connection, filename_utf8);
557 if( (retval=mpdclient_finish_command(c)) == 0 )
558 mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
559 return retval;
560 }
562 gint
563 mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename)
564 {
565 gint retval = 0;
566 gchar *filename_utf8 = locale_to_utf8(filename);
568 retval = mpdclient_cmd_save_playlist_utf8(c, filename);
569 g_free(filename_utf8);
570 return retval;
571 }
573 gint
574 mpdclient_cmd_load_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
575 {
576 mpd_sendLoadCommand(c->connection, filename_utf8);
577 c->need_update = TRUE;
578 return mpdclient_finish_command(c);
579 }
581 gint
582 mpdclient_cmd_delete_playlist_utf8(mpdclient_t *c, gchar *filename_utf8)
583 {
584 gint retval = 0;
586 mpd_sendRmCommand(c->connection, filename_utf8);
587 if( (retval=mpdclient_finish_command(c)) == 0 )
588 mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
589 return retval;
590 }
592 gint
593 mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename)
594 {
595 gint retval = 0;
596 gchar *filename_utf8 = locale_to_utf8(filename);
598 retval = mpdclient_cmd_delete_playlist_utf8(c, filename_utf8);
599 g_free(filename_utf8);
600 return retval;
601 }
604 /****************************************************************************/
605 /*** Callback managment functions *******************************************/
606 /****************************************************************************/
607 static void
608 do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data)
609 {
610 while(list)
611 {
612 mpdc_list_cb_t fn = list->data;
614 fn(c, event, data);
615 list=list->next;
616 }
617 }
619 void
620 mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data)
621 {
622 do_list_callbacks(c, c->playlist_callbacks, event, data);
623 }
625 void
626 mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb)
627 {
628 c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
629 }
631 void
632 mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb)
633 {
634 c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
635 }
637 void
638 mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data)
639 {
640 do_list_callbacks(c, c->browse_callbacks, event, data);
641 }
644 void
645 mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb)
646 {
647 c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
648 }
650 void
651 mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb)
652 {
653 c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
654 }
656 void
657 mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
658 {
659 c->error_callbacks = g_list_append(c->error_callbacks, cb);
660 }
662 void
663 mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb)
664 {
665 c->error_callbacks = g_list_remove(c->error_callbacks, cb);
666 }
668 /****************************************************************************/
669 /*** Playlist managment functions *******************************************/
670 /****************************************************************************/
672 gint
673 mpdclient_playlist_free(mpdclient_playlist_t *playlist)
674 {
675 GList *list = g_list_first(playlist->list);
677 while(list)
678 {
679 mpd_Song *song = (mpd_Song *) list->data;
680 mpd_freeSong(song);
681 list=list->next;
682 }
683 g_list_free(playlist->list);
684 memset(playlist, 0, sizeof(mpdclient_playlist_t));
685 return 0;
686 }
688 /* update playlist */
689 gint
690 mpdclient_playlist_update(mpdclient_t *c)
691 {
692 mpd_InfoEntity *entity;
694 D("mpdclient_playlist_update() [%lld]\n", c->status->playlist);
696 if (MPD_ERROR(c))
697 return -1;
699 if (c->playlist.list)
700 mpdclient_playlist_free(&c->playlist);
702 mpd_sendPlaylistInfoCommand(c->connection,-1);
703 while ((entity = mpd_getNextInfoEntity(c->connection))) {
704 if (entity->type == MPD_INFO_ENTITY_TYPE_SONG) {
705 mpd_Song *song = mpd_songDup(entity->info.song);
707 c->playlist.list = g_list_append(c->playlist.list,
708 (gpointer)song);
709 c->playlist.length++;
710 }
711 mpd_freeInfoEntity(entity);
712 }
714 c->playlist.id = c->status->playlist;
715 c->song = NULL;
716 c->playlist.updated = TRUE;
718 /* call playlist updated callbacks */
719 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
721 return mpdclient_finish_command(c);
722 }
724 #ifdef ENABLE_PLCHANGES
726 /* update playlist (plchanges) */
727 gint
728 mpdclient_playlist_update_changes(mpdclient_t *c)
729 {
730 mpd_InfoEntity *entity;
732 D("mpdclient_playlist_update_changes() [%lld -> %lld]\n",
733 c->status->playlist, c->playlist.id);
735 if (MPD_ERROR(c))
736 return -1;
738 mpd_sendPlChangesCommand(c->connection, c->playlist.id);
740 while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) {
741 mpd_Song *song = entity->info.song;
743 if (song->pos < c->playlist.length) {
744 GList *item = g_list_nth(c->playlist.list, song->pos);
746 /* update song */
747 D("updating pos:%d, id=%d [%p] - %s\n",
748 song->pos, song->id, item, song->file);
749 mpd_freeSong((mpd_Song *) item->data);
750 item->data = mpd_songDup(song);
751 } else {
752 /* add a new song */
753 D("adding song at pos %d\n", song->pos);
754 c->playlist.list = g_list_append(c->playlist.list,
755 (gpointer)mpd_songDup(song));
756 }
757 }
759 /* remove trailing songs */
760 while (c->status->playlistLength < c->playlist.length) {
761 GList *item = g_list_last(c->playlist.list);
763 /* Remove the last playlist entry */
764 D("removing song at pos %d\n", ((mpd_Song *) item->data)->pos);
765 mpd_freeSong((mpd_Song *) item->data);
766 c->playlist.list = g_list_delete_link(c->playlist.list, item);
767 c->playlist.length = g_list_length(c->playlist.list);
768 }
770 c->song = NULL;
771 c->playlist.id = c->status->playlist;
772 c->playlist.updated = TRUE;
773 c->playlist.length = g_list_length(c->playlist.list);
775 mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
777 return 0;
778 }
780 #else
781 gint
782 mpdclient_playlist_update_changes(mpdclient_t *c)
783 {
784 return mpdclient_playlist_update(c);
785 }
786 #endif
788 mpd_Song *
789 playlist_get_song(mpdclient_t *c, gint index)
790 {
791 return (mpd_Song *) g_list_nth_data(c->playlist.list, index);
792 }
794 GList *
795 playlist_lookup(mpdclient_t *c, int id)
796 {
797 GList *list = g_list_first(c->playlist.list);
799 while (list) {
800 mpd_Song *song = (mpd_Song *) list->data;
801 if( song->id == id )
802 return list;
803 list=list->next;
804 }
806 return NULL;
807 }
809 mpd_Song *
810 playlist_lookup_song(mpdclient_t *c, gint id)
811 {
812 GList *list = c->playlist.list;
814 while (list) {
815 mpd_Song *song = (mpd_Song *) list->data;
816 if (song->id == id)
817 return song;
818 list=list->next;
819 }
821 return NULL;
822 }
824 gint
825 playlist_get_index(mpdclient_t *c, mpd_Song *song)
826 {
827 return g_list_index(c->playlist.list, song);
828 }
830 gint
831 playlist_get_index_from_id(mpdclient_t *c, gint id)
832 {
833 return g_list_index(c->playlist.list, playlist_lookup_song(c, id));
834 }
836 gint
837 playlist_get_index_from_file(mpdclient_t *c, gchar *filename)
838 {
839 GList *list = c->playlist.list;
840 gint i=0;
842 while( list )
843 {
844 mpd_Song *song = (mpd_Song *) list->data;
845 if( strcmp(song->file, filename ) == 0 )
846 return i;
847 list=list->next;
848 i++;
849 }
850 return -1;
851 }
854 /****************************************************************************/
855 /*** Filelist functions *****************************************************/
856 /****************************************************************************/
858 mpdclient_filelist_t *
859 mpdclient_filelist_free(mpdclient_filelist_t *filelist)
860 {
861 GList *list = g_list_first(filelist->list);
863 D("mpdclient_filelist_free()\n");
864 if( list == NULL )
865 return NULL;
866 while( list!=NULL )
867 {
868 filelist_entry_t *entry = list->data;
870 if( entry->entity )
871 mpd_freeInfoEntity(entry->entity);
872 g_free(entry);
873 list=list->next;
874 }
875 g_list_free(filelist->list);
876 g_free(filelist->path);
877 filelist->path = NULL;
878 filelist->list = NULL;
879 filelist->length = 0;
880 g_free(filelist);
882 return NULL;
883 }
886 mpdclient_filelist_t *
887 mpdclient_filelist_get(mpdclient_t *c, gchar *path)
888 {
889 mpdclient_filelist_t *filelist;
890 mpd_InfoEntity *entity;
891 gchar *path_utf8 = locale_to_utf8(path);
892 gboolean has_dirs_only = TRUE;
894 D("mpdclient_filelist_get(%s)\n", path);
895 mpd_sendLsInfoCommand(c->connection, path_utf8);
896 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
897 if( path && path[0] && strcmp(path, "/") )
898 {
899 /* add a dummy entry for ./.. */
900 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
901 entry->entity = NULL;
902 filelist->list = g_list_append(filelist->list, (gpointer) entry);
903 filelist->length++;
904 }
906 while( (entity=mpd_getNextInfoEntity(c->connection)) )
907 {
908 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
910 entry->entity = entity;
911 filelist->list = g_list_append(filelist->list, (gpointer) entry);
912 filelist->length++;
914 if (has_dirs_only && entity->type != MPD_INFO_ENTITY_TYPE_DIRECTORY)
915 {
916 has_dirs_only = FALSE;
917 }
918 }
920 /* If there's an error, ignore it. We'll return an empty filelist. */
921 mpdclient_finish_command(c);
923 g_free(path_utf8);
924 filelist->path = g_strdup(path);
925 filelist->updated = TRUE;
927 // If there are only directory entities in the filelist, we sort it
928 if (has_dirs_only)
929 {
930 D("mpdclient_filelist_get: only dirs; sorting!\n");
931 filelist->list = g_list_sort(filelist->list, compare_filelistentry_dir);
932 }
934 return filelist;
935 }
937 mpdclient_filelist_t *
938 mpdclient_filelist_search_utf8(mpdclient_t *c,
939 int exact_match,
940 int table,
941 gchar *filter_utf8)
942 {
943 mpdclient_filelist_t *filelist;
944 mpd_InfoEntity *entity;
946 D("mpdclient_filelist_search(%s)\n", filter_utf8);
947 if( exact_match )
948 mpd_sendFindCommand(c->connection, table, filter_utf8);
949 else
950 mpd_sendSearchCommand(c->connection, table, filter_utf8);
951 filelist = g_malloc0(sizeof(mpdclient_filelist_t));
953 while( (entity=mpd_getNextInfoEntity(c->connection)) )
954 {
955 filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t));
957 entry->entity = entity;
958 filelist->list = g_list_append(filelist->list, (gpointer) entry);
959 filelist->length++;
960 }
962 if( mpdclient_finish_command(c) )
963 return mpdclient_filelist_free(filelist);
965 filelist->updated = TRUE;
967 return filelist;
968 }
971 mpdclient_filelist_t *
972 mpdclient_filelist_search(mpdclient_t *c,
973 int exact_match,
974 int table,
975 gchar *filter)
976 {
977 mpdclient_filelist_t *filelist;
978 gchar *filter_utf8 = locale_to_utf8(filter);
980 D("mpdclient_filelist_search(%s)\n", filter);
981 filelist = mpdclient_filelist_search_utf8(c, exact_match, table,
982 filter_utf8);
983 g_free(filter_utf8);
985 return filelist;
986 }
988 mpdclient_filelist_t *
989 mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist)
990 {
991 if( filelist != NULL )
992 {
993 gchar *path = g_strdup(filelist->path);
995 filelist = mpdclient_filelist_free(filelist);
996 filelist = mpdclient_filelist_get(c, path);
997 g_free(path);
998 return filelist;
999 }
1000 return NULL;
1001 }
1003 filelist_entry_t *
1004 mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song)
1005 {
1006 GList *list = g_list_first(fl->list);
1008 while( list && song)
1009 {
1010 filelist_entry_t *entry = list->data;
1011 mpd_InfoEntity *entity = entry->entity;
1013 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
1014 {
1015 mpd_Song *song2 = entity->info.song;
1017 if( strcmp(song->file, song2->file) == 0 )
1018 {
1019 return entry;
1020 }
1021 }
1022 list = list->next;
1023 }
1024 return NULL;
1025 }
1027 int
1028 mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl)
1029 {
1030 GList *list = g_list_first(fl->list);
1032 if( fl->list==NULL || fl->length<1 )
1033 return 0;
1035 mpd_sendCommandListBegin(c->connection);
1036 while( list )
1037 {
1038 filelist_entry_t *entry = list->data;
1039 mpd_InfoEntity *entity = entry->entity;
1041 if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG )
1042 {
1043 mpd_Song *song = entity->info.song;
1045 mpd_sendAddCommand(c->connection, song->file);
1046 }
1047 list = list->next;
1048 }
1049 mpd_sendCommandListEnd(c->connection);
1050 return mpdclient_finish_command(c);
1051 }
1060 GList *
1061 mpdclient_get_artists_utf8(mpdclient_t *c)
1062 {
1063 gchar *str = NULL;
1064 GList *list = NULL;
1066 D("mpdclient_get_artists()\n");
1067 mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL);
1068 while( (str=mpd_getNextArtist(c->connection)) )
1069 {
1070 list = g_list_append(list, (gpointer) str);
1071 }
1072 if( mpdclient_finish_command(c) )
1073 {
1074 return string_list_free(list);
1075 }
1077 return list;
1078 }
1080 GList *
1081 mpdclient_get_albums_utf8(mpdclient_t *c, gchar *artist_utf8)
1082 {
1083 gchar *str = NULL;
1084 GList *list = NULL;
1086 D("mpdclient_get_albums(%s)\n", artist_utf8);
1087 mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8);
1088 while( (str=mpd_getNextAlbum(c->connection)) )
1089 {
1090 list = g_list_append(list, (gpointer) str);
1091 }
1092 if( mpdclient_finish_command(c) )
1093 {
1094 return string_list_free(list);
1095 }
1097 return list;
1098 }