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.
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
,"Where 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
,"Where 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
;
155 if(mi
->CurrentState
.repeat
!= mi
->status
->repeat
)
157 what_changed
|= MPD_CST_REPEAT
;
158 mi
->CurrentState
.repeat
= mi
->status
->repeat
;
160 if(mi
->CurrentState
.random
!= mi
->status
->random
)
162 what_changed
|= MPD_CST_RANDOM
;
163 mi
->CurrentState
.random
= mi
->status
->random
;
165 if(mi
->CurrentState
.volume
!= mi
->status
->volume
)
167 what_changed
|= MPD_CST_VOLUME
;
168 mi
->CurrentState
.volume
= mi
->status
->volume
;
170 if(mi
->CurrentState
.xfade
!= mi
->status
->crossfade
)
172 what_changed
|= MPD_CST_CROSSFADE
;
173 mi
->CurrentState
.xfade
= mi
->status
->crossfade
;
175 if(mi
->CurrentState
.totaltime
!= mi
->status
->totalTime
)
177 what_changed
|= MPD_CST_TOTAL_TIME
;
178 mi
->CurrentState
.totaltime
= mi
->status
->totalTime
;
180 if(mi
->CurrentState
.elapsedtime
!= mi
->status
->elapsedTime
)
182 what_changed
|= MPD_CST_ELAPSED_TIME
;
183 mi
->CurrentState
.elapsedtime
= mi
->status
->elapsedTime
;
186 /* Check if bitrate changed, happens with vbr encodings. */
187 if(mi
->CurrentState
.bitrate
!= mi
->status
->bitRate
)
189 what_changed
|= MPD_CST_BITRATE
;
190 mi
->CurrentState
.bitrate
= mi
->status
->bitRate
;
193 /* The following 3 probly only happen on a song change, or is it possible in one song/stream? */
194 /* Check if the sample rate changed */
195 if(mi
->CurrentState
.samplerate
!= mi
->status
->sampleRate
)
197 what_changed
|= MPD_CST_AUDIOFORMAT
;
198 mi
->CurrentState
.samplerate
= mi
->status
->sampleRate
;
201 /* check if the sampling depth changed */
202 if(mi
->CurrentState
.bits
!= mi
->status
->bits
)
204 what_changed
|= MPD_CST_AUDIOFORMAT
;
205 mi
->CurrentState
.bits
= mi
->status
->bits
;
208 /* Check if the amount of audio channels changed */
209 if(mi
->CurrentState
.channels
!= mi
->status
->channels
)
211 what_changed
|= MPD_CST_AUDIOFORMAT
;
212 mi
->CurrentState
.channels
= mi
->status
->channels
;
215 if(mi
->status
->error
)
217 what_changed
|= MPD_CST_SERVER_ERROR
;
218 strcpy(mi
->CurrentState
.error
,mi
->status
->error
);
219 mpd_sendClearErrorCommand(mi
->connection
);
220 mpd_finishCommand(mi
->connection
);
224 mi
->CurrentState
.error
[0] ='\0';
227 /* Check if the updating changed,
228 * If it stopped, also update the stats for the new db-time.
230 if(mi
->CurrentState
.updatingDb
!= mi
->status
->updatingDb
)
232 what_changed
|= MPD_CST_UPDATING
;
233 if(!mi
->status
->updatingDb
)
235 mpd_stats_update_real(mi
, &what_changed
);
237 mi
->CurrentState
.updatingDb
= mi
->status
->updatingDb
;
241 mi
->CurrentState
.playlistLength
= mi
->status
->playlistLength
;
244 /* Detect changed outputs */
247 if(mi
->num_outputs
>0 )
249 mpd_OutputEntity
*output
= NULL
;
250 mpd_sendOutputsCommand(mi
->connection
);
251 while (( output
= mpd_getNextOutput(mi
->connection
)) != NULL
)
253 if(mi
->output_states
[output
->id
] != output
->enabled
)
255 mi
->output_states
[output
->id
] = output
->enabled
;
256 what_changed
|= MPD_CST_OUTPUT
;
258 mpd_freeOutputElement(output
);
260 mpd_finishCommand(mi
->connection
);
264 /* if no outputs, lets fetch them */
265 mpd_server_update_outputs(mi
);
266 if(mi
->num_outputs
== 0)
268 assert("No outputs defined? that cannot be\n");
270 what_changed
|= MPD_CST_OUTPUT
;
274 mpd_sendGetEventsCommand(mi
->connection
);
275 while((name
= mpd_getNextEvent(mi
->connection
))){
276 printf("name: %s\n", name
);
277 if(strcmp(name
, "output") == 0){
278 what_changed
|= MPD_CST_OUTPUT
;
279 }else if (strcmp(name
, "stored_playlist")==0) {
280 what_changed
|= MPD_CST_STORED_PLAYLIST
;
285 mpd_finishCommand(mi
->connection
);
289 /* Run the callback */
290 if((mi
->the_status_changed_callback
!= NULL
) && what_changed
)
292 mi
->the_status_changed_callback( mi
, what_changed
, mi
->the_status_changed_signal_userdata
);
295 /* We could have lost connection again during signal handling... so before we return check again if we are connected */
296 if(!mpd_check_connected(mi
))
298 return MPD_NOT_CONNECTED
;
303 /* returns TRUE when status is availible, when not availible and connected it tries to grab it */
304 int mpd_status_check(MpdObj
*mi
)
306 if(!mpd_check_connected(mi
))
308 debug_printf(DEBUG_INFO
,"not connected\n");
309 return MPD_NOT_CONNECTED
;
311 if(mi
->status
== NULL
)
314 if(mpd_status_update(mi
))
316 debug_printf(DEBUG_INFO
, "failed to update status\n");
317 return MPD_STATUS_FAILED
;
324 int mpd_stats_get_total_songs(MpdObj
*mi
)
328 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
329 return MPD_ARGS_ERROR
;
331 if(mpd_stats_check(mi
) != MPD_OK
)
333 debug_printf(DEBUG_ERROR
,"Failed to get status\n");
334 return MPD_STATUS_FAILED
;
336 return mi
->stats
->numberOfSongs
;
339 int mpd_stats_get_total_artists(MpdObj
*mi
)
343 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
344 return MPD_ARGS_ERROR
;
346 if(mpd_stats_check(mi
) != MPD_OK
)
348 debug_printf(DEBUG_ERROR
,"Failed to get status\n");
349 return MPD_STATS_FAILED
;
351 return mi
->stats
->numberOfArtists
;
354 int mpd_stats_get_total_albums(MpdObj
*mi
)
358 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
359 return MPD_ARGS_ERROR
;
361 if(mpd_stats_check(mi
) != MPD_OK
)
363 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
364 return MPD_STATS_FAILED
;
366 return mi
->stats
->numberOfAlbums
;
370 int mpd_stats_get_uptime(MpdObj
*mi
)
374 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
375 return MPD_ARGS_ERROR
;
377 if(mpd_stats_check(mi
) != MPD_OK
)
379 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
380 return MPD_STATS_FAILED
;
382 return mi
->stats
->uptime
;
385 int mpd_stats_get_playtime(MpdObj
*mi
)
389 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
390 return MPD_ARGS_ERROR
;
392 if(mpd_stats_check(mi
) != MPD_OK
)
394 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
395 return MPD_STATS_FAILED
;
397 return mi
->stats
->playTime
;
400 int mpd_stats_get_db_playtime(MpdObj
*mi
)
404 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
405 return MPD_ARGS_ERROR
;
407 if(mpd_stats_check(mi
) != MPD_OK
)
409 debug_printf(DEBUG_WARNING
,"Failed to get stats\n");
410 return MPD_STATS_FAILED
;
412 return mi
->stats
->dbPlayTime
;
431 int mpd_status_get_volume(MpdObj
*mi
)
435 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
436 return MPD_ARGS_ERROR
;
438 if(mpd_status_check(mi
) != MPD_OK
)
440 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
441 return MPD_STATUS_FAILED
;
443 return mi
->status
->volume
;
447 int mpd_status_get_bitrate(MpdObj
*mi
)
451 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
452 return MPD_ARGS_ERROR
;
454 if(mpd_status_check(mi
) != MPD_OK
)
456 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
457 return MPD_STATUS_FAILED
;
459 return mi
->CurrentState
.bitrate
;
462 int mpd_status_get_channels(MpdObj
*mi
)
466 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
467 return MPD_ARGS_ERROR
;
469 if(mpd_status_check(mi
) != MPD_OK
)
471 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
472 return MPD_STATUS_FAILED
;
474 return mi
->CurrentState
.channels
;
477 unsigned int mpd_status_get_samplerate(MpdObj
*mi
)
481 debug_printf(DEBUG_ERROR
,"failed to check mi == NULL\n");
482 return MPD_ARGS_ERROR
;
484 if(mpd_status_check(mi
) != MPD_OK
)
486 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
487 return MPD_STATUS_FAILED
;
489 return mi
->CurrentState
.samplerate
;
492 int mpd_status_get_bits(MpdObj
*mi
)
496 debug_printf(DEBUG_WARNING
,"failed to check mi == NULL\n");
497 return MPD_ARGS_ERROR
;
499 if(mpd_status_check(mi
) != MPD_OK
)
501 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
502 return MPD_STATUS_FAILED
;
504 return mi
->CurrentState
.bits
;
507 char * mpd_status_get_mpd_error(MpdObj
*mi
)
509 if(mi
->CurrentState
.error
[0] != '\0')
511 return strdup(mi
->CurrentState
.error
);
516 /* TODO: error checking might be nice? */
517 int mpd_status_db_is_updating(MpdObj
*mi
)
519 return mi
->CurrentState
.updatingDb
;
523 int mpd_status_get_total_song_time(MpdObj
*mi
)
525 if(!mpd_check_connected(mi
))
527 debug_printf(DEBUG_ERROR
, "failed to check mi == NULL\n");
528 return MPD_ARGS_ERROR
;
530 if(mpd_status_check(mi
) != MPD_OK
)
532 debug_printf(DEBUG_WARNING
, "Failed to get status\n");
533 return MPD_STATUS_FAILED
;
535 return mi
->status
->totalTime
;
539 int mpd_status_get_elapsed_song_time(MpdObj
*mi
)
541 if(!mpd_check_connected(mi
))
543 debug_printf(DEBUG_WARNING
,"failed to check mi == NULL\n");
544 return MPD_NOT_CONNECTED
;
546 if(mpd_status_check(mi
) != MPD_OK
)
548 debug_printf(DEBUG_WARNING
,"Failed to get status\n");
549 return MPD_STATUS_FAILED
;
551 return mi
->status
->elapsedTime
;
554 int mpd_status_set_volume(MpdObj
*mi
,int volume
)
556 if(!mpd_check_connected(mi
))
558 debug_printf(DEBUG_WARNING
,"not connected\n");
559 return MPD_NOT_CONNECTED
;
561 /* making sure volume is between 0 and 100 */
562 volume
= (volume
< 0)? 0:(volume
>100)? 100:volume
;
564 if(mpd_lock_conn(mi
))
566 debug_printf(DEBUG_ERROR
,"lock failed\n");
567 return MPD_LOCK_FAILED
;
570 /* send the command */
571 mpd_sendSetvolCommand(mi
->connection
, volume
);
572 mpd_finishCommand(mi
->connection
);
573 /* check for errors */
576 /* update status, because we changed it */
577 mpd_status_queue_update(mi
);
578 /* return current volume */
579 return mpd_status_get_volume(mi
);
582 int mpd_status_get_crossfade(MpdObj
*mi
)
584 if(!mpd_check_connected(mi
))
586 debug_printf(DEBUG_WARNING
,"not connected\n");
587 return MPD_NOT_CONNECTED
;
589 if(mpd_status_check(mi
) != MPD_OK
)
591 debug_printf(DEBUG_WARNING
,"Failed grabbing status\n");
592 return MPD_NOT_CONNECTED
;
594 return mi
->status
->crossfade
;
597 int mpd_status_set_crossfade(MpdObj
*mi
,int crossfade_time
)
599 if(!mpd_check_connected(mi
))
601 debug_printf(DEBUG_WARNING
,"not connected\n");
602 return MPD_NOT_CONNECTED
;
604 if(mpd_lock_conn(mi
))
606 debug_printf(DEBUG_ERROR
,"lock failed\n");
607 return MPD_LOCK_FAILED
;
609 mpd_sendCrossfadeCommand(mi
->connection
, crossfade_time
);
610 mpd_finishCommand(mi
->connection
);
613 mpd_status_queue_update(mi
);
618 float mpd_status_set_volume_as_float(MpdObj
*mi
, float fvol
)
620 int volume
= mpd_status_set_volume(mi
, (int)(fvol
*100.0));
623 return (float)volume
/100.0;
625 return (float)volume
;
628 int mpd_stats_update(MpdObj
*mi
)
630 return mpd_stats_update_real(mi
, NULL
);
633 int mpd_stats_update_real(MpdObj
*mi
, ChangedStatusType
* what_changed
)
635 ChangedStatusType what_changed_here
= 0;
636 if ( what_changed
== NULL
) {
637 /* we need to save the current state, because we're called standalone */
638 memcpy(&(mi
->OldState
), &(mi
->CurrentState
), sizeof(MpdServerState
));
641 if(!mpd_check_connected(mi
))
643 debug_printf(DEBUG_INFO
,"Where not connected\n");
644 return MPD_NOT_CONNECTED
;
646 if(mpd_lock_conn(mi
))
648 debug_printf(DEBUG_ERROR
,"lock failed\n");
649 return MPD_LOCK_FAILED
;
652 if(mi
->stats
!= NULL
)
654 mpd_freeStats(mi
->stats
);
656 mpd_sendStatsCommand(mi
->connection
);
657 mi
->stats
= mpd_getStats(mi
->connection
);
658 if(mi
->stats
== NULL
)
660 debug_printf(DEBUG_ERROR
,"Failed to grab stats from mpd\n");
662 else if(mi
->stats
->dbUpdateTime
!= mi
->OldState
.dbUpdateTime
)
664 debug_printf(DEBUG_INFO
, "database updated\n");
665 what_changed_here
|= MPD_CST_DATABASE
;
667 mi
->CurrentState
.dbUpdateTime
= mi
->stats
->dbUpdateTime
;
671 (*what_changed
) |= what_changed_here
;
673 if((mi
->the_status_changed_callback
!= NULL
) & what_changed_here
)
675 mi
->the_status_changed_callback(mi
, what_changed_here
, mi
->the_status_changed_signal_userdata
);
679 if(mpd_unlock_conn(mi
))
681 debug_printf(DEBUG_ERROR
, "unlock failed");
682 return MPD_LOCK_FAILED
;
688 int mpd_stats_check(MpdObj
*mi
)
690 if(!mpd_check_connected(mi
))
692 debug_printf(DEBUG_WARNING
,"not connected\n");
693 return MPD_NOT_CONNECTED
;
695 if(mi
->stats
== NULL
)
698 if(mpd_stats_update(mi
))
700 debug_printf(DEBUG_ERROR
,"failed to update status\n");
701 return MPD_STATUS_FAILED
;