1 /* libmpd (high level libmpdclient library)
2 * Copyright (C) 2004-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://gmpcwiki.sarine.nl/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "debug_printf.h"
30 #include "libmpd-internal.h"
32 int mpd_stats_update_real(MpdObj
*mi
, ChangedStatusType
* what_changed
);
34 int mpd_status_queue_update(MpdObj
*mi
)
37 if(!mpd_check_connected(mi
))
39 debug_printf(DEBUG_INFO
,"not connected\n");
40 return MPD_NOT_CONNECTED
;
42 if(mi
->status
!= NULL
)
44 mpd_freeStatus(mi
->status
);
51 int mpd_status_update(MpdObj
*mi
)
53 ChangedStatusType what_changed
=0;
54 if(!mpd_check_connected(mi
))
56 debug_printf(DEBUG_INFO
,"not connected\n");
57 return MPD_NOT_CONNECTED
;
61 debug_printf(DEBUG_ERROR
,"lock failed\n");
62 return MPD_LOCK_FAILED
;
67 if(mi
->status
!= NULL
)
69 mpd_freeStatus(mi
->status
);
72 mpd_sendStatusCommand(mi
->connection
);
73 mi
->status
= mpd_getStatus(mi
->connection
);
74 if(mi
->status
== NULL
)
76 debug_printf(DEBUG_ERROR
,"Failed to grab status from mpd\n");
78 return MPD_STATUS_FAILED
;
80 if(mpd_unlock_conn(mi
))
82 debug_printf(DEBUG_ERROR
, "Failed to unlock");
83 return MPD_LOCK_FAILED
;
88 /* first save the old status */
89 memcpy(&(mi
->OldState
), &(mi
->CurrentState
), sizeof(MpdServerState
));
92 if(mi
->CurrentState
.playlistid
!= mi
->status
->playlist
)
94 /* print debug message */
95 debug_printf(DEBUG_INFO
, "Playlist has changed!");
97 /* We can't trust the current song anymore. so we remove it */
98 /* tags might have been updated */
99 if(mi
->CurrentSong
!= NULL
)
101 mpd_freeSong(mi
->CurrentSong
);
102 mi
->CurrentSong
= NULL
;
105 /* set MPD_CST_PLAYLIST to be changed */
106 what_changed
|= MPD_CST_PLAYLIST
;
108 if(mi
->CurrentState
.playlistLength
== mi
->status
->playlistLength
)
110 // what_changed |= MPD_CST_SONGID;
113 mi
->CurrentState
.playlistid
= mi
->status
->playlist
;
116 if(mi
->CurrentState
.storedplaylistid
!= mi
->status
->storedplaylist
)
118 /* set MPD_CST_QUEUE to be changed */
119 what_changed
|= MPD_CST_STORED_PLAYLIST
;
123 mi
->CurrentState
.storedplaylistid
= mi
->status
->storedplaylist
;
128 if(mi
->CurrentState
.state
!= mi
->status
->state
)
130 what_changed
|= MPD_CST_STATE
;
131 mi
->CurrentState
.state
= mi
->status
->state
;
134 if(mi
->CurrentState
.songid
!= mi
->status
->songid
)
136 /* print debug message */
137 debug_printf(DEBUG_INFO
, "Songid has changed %i %i!", mi
->OldState
.songid
, mi
->status
->songid
);
139 what_changed
|= MPD_CST_SONGID
;
140 /* save new songid */
141 mi
->CurrentState
.songid
= mi
->status
->songid
;
144 if(mi
->CurrentState
.songpos
!= mi
->status
->song
)
146 /* print debug message */
147 debug_printf(DEBUG_INFO
, "Songpos has changed %i %i!", mi
->OldState
.songpos
, mi
->status
->song
);
149 what_changed
|= MPD_CST_SONGPOS
;
150 /* save new songid */
151 mi
->CurrentState
.songpos
= mi
->status
->song
;
154 if(mi
->CurrentState
.nextsongid
!= mi
->status
->nextsongid
|| mi
->CurrentState
.nextsongpos
!= mi
->status
->nextsong
)
156 what_changed
|= MPD_CST_NEXTSONG
;
157 /* save new songid */
158 mi
->CurrentState
.nextsongpos
= mi
->status
->nextsong
;
159 mi
->CurrentState
.nextsongid
= mi
->status
->nextsongid
;
162 if(mi
->CurrentState
.single
!= mi
->status
->single
)
164 what_changed
|= MPD_CST_SINGLE_MODE
;
165 mi
->CurrentState
.single
= mi
->status
->single
;
167 if(mi
->CurrentState
.consume
!= mi
->status
->consume
)
169 what_changed
|= MPD_CST_CONSUME_MODE
;
170 mi
->CurrentState
.consume
= mi
->status
->consume
;
172 if(mi
->CurrentState
.repeat
!= mi
->status
->repeat
)
174 what_changed
|= MPD_CST_REPEAT
;
175 mi
->CurrentState
.repeat
= mi
->status
->repeat
;
177 if(mi
->CurrentState
.random
!= mi
->status
->random
)
179 what_changed
|= MPD_CST_RANDOM
;
180 mi
->CurrentState
.random
= mi
->status
->random
;
182 if(mi
->CurrentState
.volume
!= mi
->status
->volume
)
184 what_changed
|= MPD_CST_VOLUME
;
185 mi
->CurrentState
.volume
= mi
->status
->volume
;
187 if(mi
->CurrentState
.xfade
!= mi
->status
->crossfade
)
189 what_changed
|= MPD_CST_CROSSFADE
;
190 mi
->CurrentState
.xfade
= mi
->status
->crossfade
;
192 if(mi
->CurrentState
.totaltime
!= mi
->status
->totalTime
)
194 what_changed
|= MPD_CST_TOTAL_TIME
;
195 mi
->CurrentState
.totaltime
= mi
->status
->totalTime
;
197 if(mi
->CurrentState
.elapsedtime
!= mi
->status
->elapsedTime
)
199 what_changed
|= MPD_CST_ELAPSED_TIME
;
200 mi
->CurrentState
.elapsedtime
= mi
->status
->elapsedTime
;
203 /* Check if bitrate changed, happens with vbr encodings. */
204 if(mi
->CurrentState
.bitrate
!= mi
->status
->bitRate
)
206 what_changed
|= MPD_CST_BITRATE
;
207 mi
->CurrentState
.bitrate
= mi
->status
->bitRate
;
210 /* The following 3 probly only happen on a song change, or is it possible in one song/stream? */
211 /* Check if the sample rate changed */
212 if(mi
->CurrentState
.samplerate
!= mi
->status
->sampleRate
)
214 what_changed
|= MPD_CST_AUDIOFORMAT
;
215 mi
->CurrentState
.samplerate
= mi
->status
->sampleRate
;
218 /* check if the sampling depth changed */
219 if(mi
->CurrentState
.bits
!= mi
->status
->bits
)
221 what_changed
|= MPD_CST_AUDIOFORMAT
;
222 mi
->CurrentState
.bits
= mi
->status
->bits
;
225 /* Check if the amount of audio channels changed */
226 if(mi
->CurrentState
.channels
!= mi
->status
->channels
)
228 what_changed
|= MPD_CST_AUDIOFORMAT
;
229 mi
->CurrentState
.channels
= mi
->status
->channels
;
232 if(mi
->status
->error
)
234 what_changed
|= MPD_CST_SERVER_ERROR
;
235 strcpy(mi
->CurrentState
.error
,mi
->status
->error
);
236 mpd_sendClearErrorCommand(mi
->connection
);
237 mpd_finishCommand(mi
->connection
);
241 mi
->CurrentState
.error
[0] ='\0';
244 /* Check if the updating changed,
245 * If it stopped, also update the stats for the new db-time.
247 if(mi
->CurrentState
.updatingDb
!= mi
->status
->updatingDb
)
249 what_changed
|= MPD_CST_UPDATING
;
250 if(!mi
->status
->updatingDb
)
252 mpd_stats_update_real(mi
, &what_changed
);
254 mi
->CurrentState
.updatingDb
= mi
->status
->updatingDb
;
258 mi
->CurrentState
.playlistLength
= mi
->status
->playlistLength
;
261 /* Detect changed outputs */
264 if(mi
->num_outputs
>0 )
266 mpd_OutputEntity
*output
= NULL
;
267 mpd_sendOutputsCommand(mi
->connection
);
268 while (( output
= mpd_getNextOutput(mi
->connection
)) != NULL
)
270 if(mi
->num_outputs
< output
->id
)
273 mi
->output_states
= realloc(mi
->output_states
,mi
->num_outputs
*sizeof(int));
274 mi
->output_states
[mi
->num_outputs
] = output
->enabled
;
275 what_changed
|= MPD_CST_OUTPUT
;
277 if(mi
->output_states
[output
->id
] != output
->enabled
)
279 mi
->output_states
[output
->id
] = output
->enabled
;
280 what_changed
|= MPD_CST_OUTPUT
;
282 mpd_freeOutputElement(output
);
284 mpd_finishCommand(mi
->connection
);
288 /* if no outputs, lets fetch them */
289 mpd_server_update_outputs(mi
);
290 if(mi
->num_outputs
== 0)
292 assert("No outputs defined? that cannot be\n");
294 what_changed
|= MPD_CST_OUTPUT
;
298 int update_stats
= 0;
299 mpd_sendGetEventsCommand(mi
->connection
);
300 while((name
= mpd_getNextEvent(mi
->connection
))){
301 if(strcmp(name
, "output") == 0){
302 what_changed
|= MPD_CST_OUTPUT
;
303 }else if (strcmp(name
, "database") == 0) {
304 if((what_changed
&MPD_CST_DATABASE
) == 0)
308 what_changed
|= MPD_CST_DATABASE
;
309 }else if (strcmp(name
, "stored_playlist")==0) {
310 what_changed
|= MPD_CST_STORED_PLAYLIST
;
311 }else if (strcmp(name
, "tag") == 0) {
312 what_changed
|= MPD_CST_PLAYLIST
;
313 }else if (strcmp (name
, "sticker") == 0) {
314 what_changed
|= MPD_CST_STICKER
;
315 /* This means repeat,random, replaygain or crossface changed */
316 }else if (strcmp (name
, "options") == 0) {
317 what_changed
|= MPD_CST_REPLAYGAIN
;
322 mpd_finishCommand(mi
->connection
);
324 mpd_stats_update_real(mi
, &what_changed
);
329 /* Run the callback */
330 if((mi
->the_status_changed_callback
!= NULL
) && what_changed
)
332 mi
->the_status_changed_callback( mi
, what_changed
, mi
->the_status_changed_signal_userdata
);
335 /* We could have lost connection again during signal handling... so before we return check again if we are connected */
336 if(!mpd_check_connected(mi
))
338 return MPD_NOT_CONNECTED
;
343 /* returns TRUE when status is availible, when not availible and connected it tries to grab it */
344 int mpd_status_check(MpdObj
*mi
)
346 if(!mpd_check_connected(mi
))
348 debug_printf(DEBUG_INFO
,"not connected\n");
349 return MPD_NOT_CONNECTED
;
351 if(mi
->status
== NULL
)
354 if(mpd_status_update(mi
))
356 debug_printf(DEBUG_INFO
, "failed to update status\n");
357 return MPD_STATUS_FAILED
;
364 int mpd_stats_get_total_songs(MpdObj
*mi
)
368 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
369 return MPD_ARGS_ERROR
;
371 if(mpd_stats_check(mi
) != MPD_OK
)
373 debug_printf(DEBUG_ERROR
,"Failed to get status\n");
374 return MPD_STATUS_FAILED
;
376 return mi
->stats
->numberOfSongs
;
379 int mpd_stats_get_total_artists(MpdObj
*mi
)
383 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
384 return MPD_ARGS_ERROR
;
386 if(mpd_stats_check(mi
) != MPD_OK
)
388 debug_printf(DEBUG_ERROR
,"Failed to get status\n");
389 return MPD_STATS_FAILED
;
391 return mi
->stats
->numberOfArtists
;
394 int mpd_stats_get_total_albums(MpdObj
*mi
)
398 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
399 return MPD_ARGS_ERROR
;
401 if(mpd_stats_check(mi
) != MPD_OK
)
403 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
404 return MPD_STATS_FAILED
;
406 return mi
->stats
->numberOfAlbums
;
410 int mpd_stats_get_uptime(MpdObj
*mi
)
414 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
415 return MPD_ARGS_ERROR
;
417 if(mpd_stats_check(mi
) != MPD_OK
)
419 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
420 return MPD_STATS_FAILED
;
422 return mi
->stats
->uptime
;
425 int mpd_stats_get_playtime(MpdObj
*mi
)
429 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
430 return MPD_ARGS_ERROR
;
432 if(mpd_stats_check(mi
) != MPD_OK
)
434 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
435 return MPD_STATS_FAILED
;
437 return mi
->stats
->playTime
;
440 int mpd_stats_get_db_playtime(MpdObj
*mi
)
444 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
445 return MPD_ARGS_ERROR
;
447 if(mpd_stats_check(mi
) != MPD_OK
)
449 debug_printf(DEBUG_WARNING
,"Failed to get stats\n");
450 return MPD_STATS_FAILED
;
452 return mi
->stats
->dbPlayTime
;
471 int mpd_status_get_volume(MpdObj
*mi
)
475 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
476 return MPD_ARGS_ERROR
;
478 if(mpd_status_check(mi
) != MPD_OK
)
480 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
481 return MPD_STATUS_FAILED
;
483 return mi
->status
->volume
;
487 int mpd_status_get_bitrate(MpdObj
*mi
)
491 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
492 return MPD_ARGS_ERROR
;
494 if(mpd_status_check(mi
) != MPD_OK
)
496 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
497 return MPD_STATUS_FAILED
;
499 return mi
->CurrentState
.bitrate
;
502 int mpd_status_get_channels(MpdObj
*mi
)
506 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
507 return MPD_ARGS_ERROR
;
509 if(mpd_status_check(mi
) != MPD_OK
)
511 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
512 return MPD_STATUS_FAILED
;
514 return mi
->CurrentState
.channels
;
517 unsigned int mpd_status_get_samplerate(MpdObj
*mi
)
521 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
522 return MPD_ARGS_ERROR
;
524 if(mpd_status_check(mi
) != MPD_OK
)
526 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
527 return MPD_STATUS_FAILED
;
529 return mi
->CurrentState
.samplerate
;
532 int mpd_status_get_bits(MpdObj
*mi
)
536 debug_printf(DEBUG_WARNING
,"failed to check mi == NULL\n");
537 return MPD_ARGS_ERROR
;
539 if(mpd_status_check(mi
) != MPD_OK
)
541 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
542 return MPD_STATUS_FAILED
;
544 return mi
->CurrentState
.bits
;
547 char * mpd_status_get_mpd_error(MpdObj
*mi
)
549 if(mi
->CurrentState
.error
[0] != '\0')
551 return strdup(mi
->CurrentState
.error
);
556 int mpd_status_db_is_updating(MpdObj
*mi
)
558 if(!mpd_check_connected(mi
))
560 debug_printf(DEBUG_WARNING
, "mpd_check_connected failed.\n");
563 return mi
->CurrentState
.updatingDb
;
567 int mpd_status_get_total_song_time(MpdObj
*mi
)
569 if(!mpd_check_connected(mi
))
571 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
572 return MPD_ARGS_ERROR
;
574 if(mpd_status_check(mi
) != MPD_OK
)
576 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
577 return MPD_STATUS_FAILED
;
579 return mi
->status
->totalTime
;
583 int mpd_status_get_elapsed_song_time(MpdObj
*mi
)
585 if(!mpd_check_connected(mi
))
587 debug_printf(DEBUG_WARNING
,"failed to check mi == NULL\n");
588 return MPD_NOT_CONNECTED
;
590 if(mpd_status_check(mi
) != MPD_OK
)
592 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
593 return MPD_STATUS_FAILED
;
595 return mi
->status
->elapsedTime
;
598 int mpd_status_set_volume(MpdObj
*mi
,int volume
)
600 if(!mpd_check_connected(mi
))
602 debug_printf(DEBUG_WARNING
,"not connected\n");
603 return MPD_NOT_CONNECTED
;
605 /* making sure volume is between 0 and 100 */
606 volume
= (volume
< 0)? 0:(volume
>100)? 100:volume
;
608 if(mpd_lock_conn(mi
))
610 debug_printf(DEBUG_ERROR
,"lock failed\n");
611 return MPD_LOCK_FAILED
;
614 /* send the command */
615 mpd_sendSetvolCommand(mi
->connection
, volume
);
616 mpd_finishCommand(mi
->connection
);
617 /* check for errors */
620 /* update status, because we changed it */
621 mpd_status_queue_update(mi
);
622 /* return current volume */
623 return mpd_status_get_volume(mi
);
626 int mpd_status_get_crossfade(MpdObj
*mi
)
628 if(!mpd_check_connected(mi
))
630 debug_printf(DEBUG_WARNING
,"not connected\n");
631 return MPD_NOT_CONNECTED
;
633 if(mpd_status_check(mi
) != MPD_OK
)
635 debug_printf(DEBUG_WARNING
,"Failed grabbing status\n");
636 return MPD_NOT_CONNECTED
;
638 return mi
->status
->crossfade
;
641 int mpd_status_set_crossfade(MpdObj
*mi
,int crossfade_time
)
643 if(!mpd_check_connected(mi
))
645 debug_printf(DEBUG_WARNING
,"not connected\n");
646 return MPD_NOT_CONNECTED
;
648 if(mpd_lock_conn(mi
))
650 debug_printf(DEBUG_ERROR
,"lock failed\n");
651 return MPD_LOCK_FAILED
;
653 mpd_sendCrossfadeCommand(mi
->connection
, crossfade_time
);
654 mpd_finishCommand(mi
->connection
);
657 mpd_status_queue_update(mi
);
662 float mpd_status_set_volume_as_float(MpdObj
*mi
, float fvol
)
664 int volume
= mpd_status_set_volume(mi
, (int)(fvol
*100.0));
667 return (float)volume
/100.0;
669 return (float)volume
;
672 int mpd_stats_update(MpdObj
*mi
)
674 return mpd_stats_update_real(mi
, NULL
);
677 int mpd_stats_update_real(MpdObj
*mi
, ChangedStatusType
* what_changed
)
679 ChangedStatusType what_changed_here
= 0;
680 if ( what_changed
== NULL
) {
681 /* we need to save the current state, because we're called standalone */
682 memcpy(&(mi
->OldState
), &(mi
->CurrentState
), sizeof(MpdServerState
));
685 if(!mpd_check_connected(mi
))
687 debug_printf(DEBUG_INFO
,"not connected\n");
688 return MPD_NOT_CONNECTED
;
690 if(mpd_lock_conn(mi
))
692 debug_printf(DEBUG_ERROR
,"lock failed\n");
693 return MPD_LOCK_FAILED
;
696 if(mi
->stats
!= NULL
)
698 mpd_freeStats(mi
->stats
);
700 mpd_sendStatsCommand(mi
->connection
);
701 mi
->stats
= mpd_getStats(mi
->connection
);
702 if(mi
->stats
== NULL
)
704 debug_printf(DEBUG_ERROR
,"Failed to grab stats from mpd\n");
706 else if(mi
->stats
->dbUpdateTime
!= mi
->OldState
.dbUpdateTime
)
708 debug_printf(DEBUG_INFO
, "database updated\n");
709 what_changed_here
|= MPD_CST_DATABASE
;
711 mi
->CurrentState
.dbUpdateTime
= mi
->stats
->dbUpdateTime
;
715 (*what_changed
) |= what_changed_here
;
717 if((mi
->the_status_changed_callback
!= NULL
) & what_changed_here
)
719 mi
->the_status_changed_callback(mi
, what_changed_here
, mi
->the_status_changed_signal_userdata
);
723 if(mpd_unlock_conn(mi
))
725 debug_printf(DEBUG_ERROR
, "unlock failed");
726 return MPD_LOCK_FAILED
;
732 int mpd_stats_check(MpdObj
*mi
)
734 if(!mpd_check_connected(mi
))
736 debug_printf(DEBUG_WARNING
,"not connected\n");
737 return MPD_NOT_CONNECTED
;
739 if(mi
->stats
== NULL
)
742 if(mpd_stats_update(mi
))
744 debug_printf(DEBUG_ERROR
,"failed to update status\n");
745 return MPD_STATUS_FAILED
;