2 *Copyright (C) 2004-2007 Qball Cow <Qball@qballcow.nl>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
29 #include "debug_printf.h"
31 #include "libmpd-internal.h"
33 int mpd_stats_update_real(MpdObj
*mi
, ChangedStatusType
* what_changed
);
35 int mpd_status_queue_update(MpdObj
*mi
)
38 if(!mpd_check_connected(mi
))
40 debug_printf(DEBUG_INFO
,"Where not connected\n");
41 return MPD_NOT_CONNECTED
;
43 if(mi
->status
!= NULL
)
45 mpd_freeStatus(mi
->status
);
52 int mpd_status_update(MpdObj
*mi
)
54 ChangedStatusType what_changed
=0;
55 if(!mpd_check_connected(mi
))
57 debug_printf(DEBUG_INFO
,"Where not connected\n");
58 return MPD_NOT_CONNECTED
;
62 debug_printf(DEBUG_ERROR
,"lock failed\n");
63 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
;
122 mi
->CurrentState
.storedplaylistid
= mi
->status
->storedplaylist
;
127 if(mi
->CurrentState
.state
!= mi
->status
->state
)
129 what_changed
|= MPD_CST_STATE
;
130 mi
->CurrentState
.state
= mi
->status
->state
;
133 if(mi
->CurrentState
.songid
!= mi
->status
->songid
)
135 /* print debug message */
136 debug_printf(DEBUG_INFO
, "Songid has changed %i %i!", mi
->OldState
.songid
, mi
->status
->songid
);
138 what_changed
|= MPD_CST_SONGID
;
139 /* save new songid */
140 mi
->CurrentState
.songid
= mi
->status
->songid
;
143 if(mi
->CurrentState
.songpos
!= mi
->status
->song
)
145 /* print debug message */
146 debug_printf(DEBUG_INFO
, "Songpos has changed %i %i!", mi
->OldState
.songpos
, mi
->status
->song
);
148 what_changed
|= MPD_CST_SONGPOS
;
149 /* save new songid */
150 mi
->CurrentState
.songpos
= mi
->status
->song
;
154 if(mi
->CurrentState
.repeat
!= mi
->status
->repeat
)
156 what_changed
|= MPD_CST_REPEAT
;
157 mi
->CurrentState
.repeat
= mi
->status
->repeat
;
159 if(mi
->CurrentState
.random
!= mi
->status
->random
)
161 what_changed
|= MPD_CST_RANDOM
;
162 mi
->CurrentState
.random
= mi
->status
->random
;
164 if(mi
->CurrentState
.volume
!= mi
->status
->volume
)
166 what_changed
|= MPD_CST_VOLUME
;
167 mi
->CurrentState
.volume
= mi
->status
->volume
;
169 if(mi
->CurrentState
.xfade
!= mi
->status
->crossfade
)
171 what_changed
|= MPD_CST_CROSSFADE
;
172 mi
->CurrentState
.xfade
= mi
->status
->crossfade
;
174 if(mi
->CurrentState
.totaltime
!= mi
->status
->totalTime
)
176 what_changed
|= MPD_CST_TOTAL_TIME
;
177 mi
->CurrentState
.totaltime
= mi
->status
->totalTime
;
179 if(mi
->CurrentState
.elapsedtime
!= mi
->status
->elapsedTime
)
181 what_changed
|= MPD_CST_ELAPSED_TIME
;
182 mi
->CurrentState
.elapsedtime
= mi
->status
->elapsedTime
;
185 /* Check if bitrate changed, happens with vbr encodings. */
186 if(mi
->CurrentState
.bitrate
!= mi
->status
->bitRate
)
188 what_changed
|= MPD_CST_BITRATE
;
189 mi
->CurrentState
.bitrate
= mi
->status
->bitRate
;
192 /* The following 3 probly only happen on a song change, or is it possible in one song/stream? */
193 /* Check if the sample rate changed */
194 if(mi
->CurrentState
.samplerate
!= mi
->status
->sampleRate
)
196 what_changed
|= MPD_CST_AUDIOFORMAT
;
197 mi
->CurrentState
.samplerate
= mi
->status
->sampleRate
;
200 /* check if the sampling depth changed */
201 if(mi
->CurrentState
.bits
!= mi
->status
->bits
)
203 what_changed
|= MPD_CST_AUDIOFORMAT
;
204 mi
->CurrentState
.bits
= mi
->status
->bits
;
207 /* Check if the amount of audio channels changed */
208 if(mi
->CurrentState
.channels
!= mi
->status
->channels
)
210 what_changed
|= MPD_CST_AUDIOFORMAT
;
211 mi
->CurrentState
.channels
= mi
->status
->channels
;
214 if(mi
->status
->error
)
216 what_changed
|= MPD_CST_SERVER_ERROR
;
217 strcpy(mi
->CurrentState
.error
,mi
->status
->error
);
218 mpd_sendClearErrorCommand(mi
->connection
);
219 mpd_finishCommand(mi
->connection
);
223 mi
->CurrentState
.error
[0] ='\0';
226 /* Check if the updating changed,
227 * If it stopped, also update the stats for the new db-time.
229 if(mi
->CurrentState
.updatingDb
!= mi
->status
->updatingDb
)
231 what_changed
|= MPD_CST_UPDATING
;
232 if(!mi
->status
->updatingDb
)
234 mpd_stats_update_real(mi
, &what_changed
);
236 mi
->CurrentState
.updatingDb
= mi
->status
->updatingDb
;
240 mi
->CurrentState
.playlistLength
= mi
->status
->playlistLength
;
243 /* Detect changed outputs */
244 if(mi
->num_outputs
>0 )
246 mpd_OutputEntity
*output
= NULL
;
247 mpd_sendOutputsCommand(mi
->connection
);
248 while (( output
= mpd_getNextOutput(mi
->connection
)) != NULL
)
250 if(mi
->output_states
[output
->id
] != output
->enabled
)
252 mi
->output_states
[output
->id
] = output
->enabled
;
253 what_changed
|= MPD_CST_OUTPUT
;
255 mpd_freeOutputElement(output
);
257 mpd_finishCommand(mi
->connection
);
261 /* if no outputs, lets fetch them */
262 mpd_server_update_outputs(mi
);
263 if(mi
->num_outputs
== 0)
265 assert("No outputs defined? that cannot be\n");
267 what_changed
|= MPD_CST_OUTPUT
;
270 /* Run the callback */
271 if((mi
->the_status_changed_callback
!= NULL
) && what_changed
)
273 mi
->the_status_changed_callback( mi
, what_changed
, mi
->the_status_changed_signal_userdata
);
276 /* We could have lost connection again during signal handling... so before we return check again if we are connected */
277 if(!mpd_check_connected(mi
))
279 return MPD_NOT_CONNECTED
;
284 /* returns TRUE when status is availible, when not availible and connected it tries to grab it */
285 int mpd_status_check(MpdObj
*mi
)
287 if(!mpd_check_connected(mi
))
289 debug_printf(DEBUG_INFO
,"not connected\n");
290 return MPD_NOT_CONNECTED
;
292 if(mi
->status
== NULL
)
295 if(mpd_status_update(mi
))
297 debug_printf(DEBUG_INFO
, "failed to update status\n");
298 return MPD_STATUS_FAILED
;
305 int mpd_stats_get_total_songs(MpdObj
*mi
)
309 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
310 return MPD_ARGS_ERROR
;
312 if(mpd_stats_check(mi
) != MPD_OK
)
314 debug_printf(DEBUG_ERROR
,"Failed to get status\n");
315 return MPD_STATUS_FAILED
;
317 return mi
->stats
->numberOfSongs
;
320 int mpd_stats_get_total_artists(MpdObj
*mi
)
324 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
325 return MPD_ARGS_ERROR
;
327 if(mpd_stats_check(mi
) != MPD_OK
)
329 debug_printf(DEBUG_ERROR
,"Failed to get status\n");
330 return MPD_STATS_FAILED
;
332 return mi
->stats
->numberOfArtists
;
335 int mpd_stats_get_total_albums(MpdObj
*mi
)
339 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
340 return MPD_ARGS_ERROR
;
342 if(mpd_stats_check(mi
) != MPD_OK
)
344 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
345 return MPD_STATS_FAILED
;
347 return mi
->stats
->numberOfAlbums
;
351 int mpd_stats_get_uptime(MpdObj
*mi
)
355 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
356 return MPD_ARGS_ERROR
;
358 if(mpd_stats_check(mi
) != MPD_OK
)
360 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
361 return MPD_STATS_FAILED
;
363 return mi
->stats
->uptime
;
366 int mpd_stats_get_playtime(MpdObj
*mi
)
370 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
371 return MPD_ARGS_ERROR
;
373 if(mpd_stats_check(mi
) != MPD_OK
)
375 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
376 return MPD_STATS_FAILED
;
378 return mi
->stats
->playTime
;
381 int mpd_stats_get_db_playtime(MpdObj
*mi
)
385 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
386 return MPD_ARGS_ERROR
;
388 if(mpd_stats_check(mi
) != MPD_OK
)
390 debug_printf(DEBUG_WARNING
,"Failed to get stats\n");
391 return MPD_STATS_FAILED
;
393 return mi
->stats
->dbPlayTime
;
412 int mpd_status_get_volume(MpdObj
*mi
)
416 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
417 return MPD_ARGS_ERROR
;
419 if(mpd_status_check(mi
) != MPD_OK
)
421 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
422 return MPD_STATUS_FAILED
;
424 return mi
->status
->volume
;
428 int mpd_status_get_bitrate(MpdObj
*mi
)
432 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
433 return MPD_ARGS_ERROR
;
435 if(mpd_status_check(mi
) != MPD_OK
)
437 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
438 return MPD_STATUS_FAILED
;
440 return mi
->CurrentState
.bitrate
;
443 int mpd_status_get_channels(MpdObj
*mi
)
447 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
448 return MPD_ARGS_ERROR
;
450 if(mpd_status_check(mi
) != MPD_OK
)
452 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
453 return MPD_STATUS_FAILED
;
455 return mi
->CurrentState
.channels
;
458 unsigned int mpd_status_get_samplerate(MpdObj
*mi
)
462 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
463 return MPD_ARGS_ERROR
;
465 if(mpd_status_check(mi
) != MPD_OK
)
467 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
468 return MPD_STATUS_FAILED
;
470 return mi
->CurrentState
.samplerate
;
473 int mpd_status_get_bits(MpdObj
*mi
)
477 debug_printf(DEBUG_WARNING
,"failed to check mi == NULL\n");
478 return MPD_ARGS_ERROR
;
480 if(mpd_status_check(mi
) != MPD_OK
)
482 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
483 return MPD_STATUS_FAILED
;
485 return mi
->CurrentState
.bits
;
488 char * mpd_status_get_mpd_error(MpdObj
*mi
)
490 if(mi
->CurrentState
.error
[0] != '\0')
492 return strdup(mi
->CurrentState
.error
);
497 /* TODO: error checking might be nice? */
498 int mpd_status_db_is_updating(MpdObj
*mi
)
500 return mi
->CurrentState
.updatingDb
;
504 int mpd_status_get_total_song_time(MpdObj
*mi
)
506 if(!mpd_check_connected(mi
))
508 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
509 return MPD_ARGS_ERROR
;
511 if(mpd_status_check(mi
) != MPD_OK
)
513 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
514 return MPD_STATUS_FAILED
;
516 return mi
->status
->totalTime
;
520 int mpd_status_get_elapsed_song_time(MpdObj
*mi
)
522 if(!mpd_check_connected(mi
))
524 debug_printf(DEBUG_WARNING
,"failed to check mi == NULL\n");
525 return MPD_NOT_CONNECTED
;
527 if(mpd_status_check(mi
) != MPD_OK
)
529 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
530 return MPD_STATUS_FAILED
;
532 return mi
->status
->elapsedTime
;
535 int mpd_status_set_volume(MpdObj
*mi
,int volume
)
537 if(!mpd_check_connected(mi
))
539 debug_printf(DEBUG_WARNING
,"not connected\n");
540 return MPD_NOT_CONNECTED
;
542 /* making sure volume is between 0 and 100 */
543 volume
= (volume
< 0)? 0:(volume
>100)? 100:volume
;
545 if(mpd_lock_conn(mi
))
547 debug_printf(DEBUG_ERROR
,"lock failed\n");
548 return MPD_LOCK_FAILED
;
551 /* send the command */
552 mpd_sendSetvolCommand(mi
->connection
, volume
);
553 mpd_finishCommand(mi
->connection
);
554 /* check for errors */
557 /* update status, because we changed it */
558 mpd_status_queue_update(mi
);
559 /* return current volume */
560 return mpd_status_get_volume(mi
);
563 int mpd_status_get_crossfade(MpdObj
*mi
)
565 if(!mpd_check_connected(mi
))
567 debug_printf(DEBUG_WARNING
,"not connected\n");
568 return MPD_NOT_CONNECTED
;
570 if(mpd_status_check(mi
) != MPD_OK
)
572 debug_printf(DEBUG_WARNING
,"Failed grabbing status\n");
573 return MPD_NOT_CONNECTED
;
575 return mi
->status
->crossfade
;
578 int mpd_status_set_crossfade(MpdObj
*mi
,int crossfade_time
)
580 if(!mpd_check_connected(mi
))
582 debug_printf(DEBUG_WARNING
,"not connected\n");
583 return MPD_NOT_CONNECTED
;
585 if(mpd_lock_conn(mi
))
587 debug_printf(DEBUG_ERROR
,"lock failed\n");
588 return MPD_LOCK_FAILED
;
590 mpd_sendCrossfadeCommand(mi
->connection
, crossfade_time
);
591 mpd_finishCommand(mi
->connection
);
594 mpd_status_queue_update(mi
);
599 float mpd_status_set_volume_as_float(MpdObj
*mi
, float fvol
)
601 int volume
= mpd_status_set_volume(mi
, (int)(fvol
*100.0));
604 return (float)volume
/100.0;
606 return (float)volume
;
609 int mpd_stats_update(MpdObj
*mi
)
611 return mpd_stats_update_real(mi
, NULL
);
614 int mpd_stats_update_real(MpdObj
*mi
, ChangedStatusType
* what_changed
)
616 ChangedStatusType what_changed_here
= 0;
617 if ( what_changed
== NULL
) {
618 /* we need to save the current state, because we're called standalone */
619 memcpy(&(mi
->OldState
), &(mi
->CurrentState
), sizeof(MpdServerState
));
622 if(!mpd_check_connected(mi
))
624 debug_printf(DEBUG_INFO
,"Where not connected\n");
625 return MPD_NOT_CONNECTED
;
627 if(mpd_lock_conn(mi
))
629 debug_printf(DEBUG_ERROR
,"lock failed\n");
630 return MPD_LOCK_FAILED
;
633 if(mi
->stats
!= NULL
)
635 mpd_freeStats(mi
->stats
);
637 mpd_sendStatsCommand(mi
->connection
);
638 mi
->stats
= mpd_getStats(mi
->connection
);
639 if(mi
->stats
== NULL
)
641 debug_printf(DEBUG_ERROR
,"Failed to grab stats from mpd\n");
643 else if(mi
->stats
->dbUpdateTime
!= mi
->OldState
.dbUpdateTime
)
645 debug_printf(DEBUG_INFO
, "database updated\n");
646 what_changed_here
|= MPD_CST_DATABASE
;
648 mi
->CurrentState
.dbUpdateTime
= mi
->stats
->dbUpdateTime
;
652 (*what_changed
) |= what_changed_here
;
654 if((mi
->the_status_changed_callback
!= NULL
) & what_changed_here
)
656 mi
->the_status_changed_callback(mi
, what_changed_here
, mi
->the_status_changed_signal_userdata
);
660 if(mpd_unlock_conn(mi
))
662 debug_printf(DEBUG_ERROR
, "unlock failed");
663 return MPD_LOCK_FAILED
;
669 int mpd_stats_check(MpdObj
*mi
)
671 if(!mpd_check_connected(mi
))
673 debug_printf(DEBUG_WARNING
,"not connected\n");
674 return MPD_NOT_CONNECTED
;
676 if(mi
->stats
== NULL
)
679 if(mpd_stats_update(mi
))
681 debug_printf(DEBUG_ERROR
,"failed to update status\n");
682 return MPD_STATUS_FAILED
;