add comment to strfsong parser
[libmpd.git] / src / libmpd-status.c
blob88f0db24e02780f3070c2fa8391b01fc65d1642b
1 /*
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.
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <assert.h>
23 #define __USE_GNU
25 #include <string.h>
26 #include <regex.h>
27 #include <stdarg.h>
28 #include <config.h>
29 #include "debug_printf.h"
30 #include "libmpd.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);
46 mi->status = NULL;
48 return MPD_OK;
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;
60 if(mpd_lock_conn(mi))
62 debug_printf(DEBUG_ERROR,"lock failed\n");
63 return MPD_LOCK_FAILED;
67 if(mi->status != NULL)
69 mpd_freeStatus(mi->status);
70 mi->status = NULL;
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");
77 mpd_unlock_conn(mi);
78 return MPD_STATUS_FAILED;
80 if(mpd_unlock_conn(mi))
82 debug_printf(DEBUG_ERROR, "Failed to unlock");
83 return MPD_LOCK_FAILED;
86 * check for changes
88 /* first save the old status */
89 memcpy(&(mi->OldState), &(mi->CurrentState), sizeof(MpdServerState));
91 /* playlist change */
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;
112 /* save new id */
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;
121 /* save new id */
122 mi->CurrentState.storedplaylistid = mi->status->storedplaylist;
126 /* state change */
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);
221 else
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);
259 else
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;
281 return MPD_OK;
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)
294 /* try to update */
295 if(mpd_status_update(mi))
297 debug_printf(DEBUG_INFO, "failed to update status\n");
298 return MPD_STATUS_FAILED;
301 return MPD_OK;
305 int mpd_stats_get_total_songs(MpdObj *mi)
307 if(mi == NULL)
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)
322 if(mi == NULL)
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)
337 if(mi == NULL)
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)
353 if(mi == NULL)
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)
368 if(mi == NULL)
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)
383 if(mi == NULL)
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)
414 if(mi == NULL)
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)
430 if(mi == NULL)
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)
445 if(mi == NULL)
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)
460 if(mi == NULL)
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)
475 if(mi == NULL)
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);
494 return NULL;
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 */
556 mpd_unlock_conn(mi);
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);
593 mpd_unlock_conn(mi);
594 mpd_status_queue_update(mi);
595 return MPD_OK;
599 float mpd_status_set_volume_as_float(MpdObj *mi, float fvol)
601 int volume = mpd_status_set_volume(mi, (int)(fvol*100.0));
602 if(volume > -1)
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;
651 if (what_changed) {
652 (*what_changed) |= what_changed_here;
653 } else {
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;
665 return MPD_OK;
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)
678 /* try to update */
679 if(mpd_stats_update(mi))
681 debug_printf(DEBUG_ERROR,"failed to update status\n");
682 return MPD_STATUS_FAILED;
685 return MPD_OK;