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"
31 #include "libmpd-internal.h"
33 static void mpd_free_queue_ob(MpdObj
*mi
);
34 static void mpd_server_free_commands(MpdObj
*mi
);
37 char *libmpd_version
= LIBMPD_VERSION
;
40 * Not every platfarm has strndup, so here we have a nice little custom implementation
42 char * strndup(const char *s
, size_t n
)
51 /* nAvail = min( strlen(s)+1, n+1 ); */
52 nAvail
=((strlen(s
)+1) > (n
+1)) ? n
+1 : strlen(s
)+1;
53 if(!(p
=malloc(nAvail
))) {
62 * @param state a #MpdServerState to initialize
64 * Initialize #MpdServerState. To stop duplicating code.
66 static void mpd_init_MpdServerState(MpdServerState
*state
)
68 state
->playlistid
= -1;
69 state
->storedplaylistid
= -1;
73 state
->nextsongpos
= -1;
74 state
->nextsongid
= -1;
75 state
->dbUpdateTime
= 0;
76 state
->updatingDb
= 0;
82 state
->elapsedtime
= 0;
84 state
->samplerate
= 0;
89 state
->playlistLength
= 0;
90 state
->error
[0] = '\0';
95 static MpdObj
* mpd_create()
97 MpdObj
* mi
= g_slice_new0(MpdObj
);
98 g_return_val_if_fail(mi
!= NULL
, NULL
); /* should never happen on linux */
100 /* set default values */
101 /* we start not connected */
102 mi
->connected
= FALSE
;
103 /* port 6600 is the default mpd port */
106 mi
->hostname
= strdup("localhost");
107 /* 1 second timeout */
108 mi
->connection_timeout
= 1.0;
110 mpd_init_MpdServerState(&(mi
->CurrentState
));
111 mpd_init_MpdServerState(&(mi
->OldState
));
113 /* connection is locked because where not connected */
114 mi
->connection_lock
= TRUE
;
117 mi
->search_type
= MPD_SEARCH_TYPE_NONE
;
118 /* no need to initialize, but set it to anything anyway*/
119 mi
->search_field
= MPD_TAG_ITEM_ARTIST
;
124 void mpd_free(MpdObj
*mi
)
126 debug_printf(DEBUG_INFO
, "destroying MpdObj object\n");
130 debug_printf(DEBUG_WARNING
, "Connection still running, disconnecting\n");
148 mpd_closeConnection(mi
->connection
);
152 mpd_freeStatus(mi
->status
);
156 mpd_freeStats(mi
->stats
);
160 mpd_freeSong(mi
->CurrentSong
);
164 g_strfreev(mi
->url_handlers
);
165 mi
->url_handlers
= NULL
;
167 mpd_free_queue_ob(mi
);
168 mpd_server_free_commands(mi
);
169 g_slice_free(MpdObj
, mi
);
172 int mpd_check_error(MpdObj
*mi
)
176 debug_printf(DEBUG_ERROR
, "mi == NULL?");
177 return MPD_ARGS_ERROR
;
180 /* this shouldn't happen, ever */
181 if(mi
->connection
== NULL
)
183 debug_printf(DEBUG_ERROR
, "mi->connection == NULL?");
184 return MPD_FATAL_ERROR
;
187 /* TODO: map these errors in the future */
188 mi
->error
= mi
->connection
->error
;
189 mi
->error_mpd_code
= mi
->connection
->errorCode
;
190 /*TODO: do I need to strdup this? */
191 if(!g_utf8_validate(mi
->connection
->errorStr
, -1, NULL
)){
192 mi
->error_msg
= g_locale_to_utf8(mi
->connection
->errorStr
, -1, NULL
, NULL
,NULL
);
194 mi
->error_msg
= g_strdup(mi
->connection
->errorStr
);
197 if(mi
->error_msg
== NULL
) mi
->error_msg
= g_strdup("Failed to convert error message to utf-8");
199 /* Check for permission */
200 /* First check for an error reported by MPD
201 * Then check what type of error mpd reported
203 if(mi
->error
== MPD_ERROR_ACK
)
206 debug_printf(DEBUG_ERROR
,"clearing errors in mpd_Connection: %i-%s", mi
->connection
->errorCode
, mi
->connection
->errorStr
);
207 mpd_clearError(mi
->connection
);
208 if (mi
->the_error_callback
)
210 debug_printf(DEBUG_ERROR
, "Error callback 1 (ACK)");
211 if(mi
->the_error_callback(mi
, mi
->error_mpd_code
, mi
->error_msg
, mi
->the_error_signal_userdata
))
213 debug_printf(DEBUG_ERROR
, "Error callback told me to disconnect");
216 mi
->error_msg
= NULL
;
218 return MPD_SERVER_ERROR
;
222 mi
->error_msg
= NULL
;
228 debug_printf(DEBUG_ERROR
, "Following error occurred: %i: code: %i msg: %s", mi
->error
,mi
->connection
->errorCode
, mi
->error_msg
);
230 if (mi
->the_error_callback
)
232 debug_printf(DEBUG_ERROR
, "Error callback 2");
233 mi
->the_error_callback(mi
, mi
->error
, mi
->error_msg
, mi
->the_error_signal_userdata
);
237 mi
->error_msg
= NULL
;
239 return MPD_SERVER_ERROR
;
242 mi
->error_msg
= NULL
;
248 int mpd_lock_conn(MpdObj
*mi
)
251 if(mi
->connection_lock
)
253 debug_printf(DEBUG_WARNING
, "Failed to lock connection, already locked\n");
254 return MPD_LOCK_FAILED
;
256 mi
->connection_lock
= TRUE
;
260 int mpd_unlock_conn(MpdObj
*mi
)
262 if(!mi
->connection_lock
)
264 debug_printf(DEBUG_ERROR
, "Failed to unlock connection, already unlocked\n");
265 return MPD_LOCK_FAILED
;
268 mi
->connection_lock
= FALSE
;
270 return mpd_check_error(mi
);
273 MpdObj
* mpd_new_default()
275 debug_printf(DEBUG_INFO
, "creating a new mpdInt object\n");
279 MpdObj
*mpd_new(char *hostname
, int port
, char *password
)
281 MpdObj
*mi
= mpd_create();
288 mpd_set_hostname(mi
, hostname
);
292 mpd_set_port(mi
, port
);
296 mpd_set_password(mi
, password
);
302 const char * mpd_get_hostname(MpdObj
*mi
)
311 int mpd_set_hostname(MpdObj
*mi
, char *hostname
)
315 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
316 return MPD_ARGS_ERROR
;
319 if(mi
->hostname
!= NULL
)
323 /* possible location todo some post processing of hostname */
324 mi
->hostname
= strdup(hostname
);
328 int mpd_set_password(MpdObj
*mi
, const char *password
)
332 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
333 return MPD_ARGS_ERROR
;
336 if(mi
->password
!= NULL
)
340 /* possible location todo some post processing of password */
341 mi
->password
= strdup(password
);
346 int mpd_send_password(MpdObj
*mi
)
348 if(!mi
) return MPD_ARGS_ERROR
;
349 if(mi
->password
&& mpd_check_connected(mi
) && strlen(mi
->password
))
351 if(mpd_lock_conn(mi
))
353 debug_printf(DEBUG_WARNING
, "failed to lock connection");
354 return MPD_LOCK_FAILED
;
356 mpd_sendPasswordCommand(mi
->connection
, mi
->password
);
357 mpd_finishCommand(mi
->connection
);
358 if(mpd_unlock_conn(mi
))
360 debug_printf(DEBUG_ERROR
, "Failed to unlock connection\n");
361 return MPD_LOCK_FAILED
;
363 mpd_server_get_allowed_commands(mi
);
364 /*TODO: should I do it here, or in the
365 * mpd_server_get_allowed_command, so it also get's executed on
368 if((mi
->the_status_changed_callback
!= NULL
))
370 /** update the supported tags */
373 char **retv
= mpd_server_get_tag_types(mi
);
375 for(i
=0;i
<MPD_TAG_ITEM_ANY
;i
++)
378 for(j
=0;retv
[j
] && strcasecmp(retv
[j
],mpdTagItemKeys
[i
]); j
++);
379 if(retv
[j
]) mi
->supported_tags
[i
] = TRUE
;
380 else mi
->supported_tags
[i
] = FALSE
;
384 /* also always true */
385 mi
->supported_tags
[MPD_TAG_ITEM_FILENAME
] = TRUE
;
386 mi
->supported_tags
[MPD_TAG_ITEM_ANY
] = TRUE
;
388 /* If permission updates, we should also call an output update, The data might be available now. */
389 mi
->the_status_changed_callback( mi
,
390 MPD_CST_PERMISSION
|MPD_CST_OUTPUT
, mi
->the_status_changed_signal_userdata
);
396 int mpd_set_port(MpdObj
*mi
, int port
)
400 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
401 return MPD_ARGS_ERROR
;
407 int mpd_set_connection_timeout(MpdObj
*mi
, float timeout
)
411 debug_printf(DEBUG_ERROR
, "mi == NULL\n");
412 return MPD_ARGS_ERROR
;
414 mi
->connection_timeout
= timeout
;
415 if(mpd_check_connected(mi
))
417 /*TODO: set timeout */
418 if(mpd_lock_conn(mi
))
420 debug_printf(DEBUG_ERROR
,"lock failed\n");
421 return MPD_LOCK_FAILED
;
423 mpd_setConnectionTimeout(mi
->connection
, timeout
);
424 mpd_finishCommand(mi
->connection
);
432 static void mpd_server_free_commands(MpdObj
*mi
)
437 while(mi
->commands
[i
].command_name
)
439 free(mi
->commands
[i
].command_name
);
447 char *mpd_server_get_version(MpdObj
*mi
)
450 if(!mi
|| !mpd_check_connected(mi
))
452 retval
= malloc(10*sizeof(char));
453 snprintf(retval
,10,"%i.%i.%i", mi
->connection
->version
[0], mi
->connection
->version
[1], mi
->connection
->version
[2]);
454 /* always make sure the string is terminated */
459 int mpd_server_get_allowed_commands(MpdObj
*mi
)
462 int num_commands
= 0;
464 debug_printf(DEBUG_ERROR
, "mi != NULL failed\n");
465 return MPD_ARGS_ERROR
;
467 if(!mpd_check_connected(mi
)) {
468 debug_printf(DEBUG_WARNING
, "Not Connected");
469 return MPD_NOT_CONNECTED
;
471 if(!mpd_server_check_version(mi
,0,12,0)){
472 debug_printf(DEBUG_INFO
, "Not supported by mpd");
473 return MPD_SERVER_NOT_SUPPORTED
;
476 mpd_server_free_commands(mi
);
478 if(mpd_lock_conn(mi
))
480 debug_printf(DEBUG_ERROR
, "lock failed");
481 return MPD_LOCK_FAILED
;
483 mpd_sendCommandsCommand(mi
->connection
);
484 while((temp
= mpd_getNextCommand(mi
->connection
)))
487 mi
->commands
= realloc(mi
->commands
, (num_commands
+1)*sizeof(MpdCommand
));
488 mi
->commands
[num_commands
-1].command_name
= temp
;
489 mi
->commands
[num_commands
-1].enabled
= TRUE
;
490 mi
->commands
[num_commands
].command_name
= NULL
;
491 mi
->commands
[num_commands
].enabled
= FALSE
;
492 if(strcmp(mi
->commands
[num_commands
-1].command_name
, "idle") == 0) {
496 mpd_finishCommand(mi
->connection
);
497 mpd_sendNotCommandsCommand(mi
->connection
);
498 while((temp
= mpd_getNextCommand(mi
->connection
)))
501 mi
->commands
= realloc(mi
->commands
, (num_commands
+1)*sizeof(MpdCommand
));
502 mi
->commands
[num_commands
-1].command_name
= temp
;
503 mi
->commands
[num_commands
-1].enabled
= FALSE
;
504 mi
->commands
[num_commands
].command_name
= NULL
;
505 mi
->commands
[num_commands
].enabled
= FALSE
;
507 mpd_finishCommand(mi
->connection
);
509 if(mpd_unlock_conn(mi
))
511 return MPD_LOCK_FAILED
;
518 int mpd_disconnect(MpdObj
*mi
)
523 debug_printf(DEBUG_INFO
, "disconnecting\n");
527 mpd_closeConnection(mi
->connection
);
528 mi
->connection
= NULL
;
532 mpd_freeStatus(mi
->status
);
537 mpd_freeStats(mi
->stats
);
542 mpd_freeSong(mi
->CurrentSong
);
543 mi
->CurrentSong
= NULL
;
547 g_strfreev(mi
->url_handlers
);
548 mi
->url_handlers
= NULL
;
550 mi
->CurrentState
.playlistid
= -1;
551 mi
->CurrentState
.storedplaylistid
= -1;
552 mi
->CurrentState
.state
= -1;
553 mi
->CurrentState
.songid
= -1;
554 mi
->CurrentState
.songpos
= -1;
555 mi
->CurrentState
.nextsongid
= -1;
556 mi
->CurrentState
.nextsongpos
= -1;
557 mi
->CurrentState
.dbUpdateTime
= 0;
558 mi
->CurrentState
.updatingDb
= 0;
559 mi
->CurrentState
.repeat
= -1;
560 mi
->CurrentState
.random
= -1;
561 mi
->CurrentState
.volume
= -2;
562 mi
->CurrentState
.xfade
= -1;
563 mi
->CurrentState
.totaltime
= 0;
564 mi
->CurrentState
.elapsedtime
= 0;
565 mi
->CurrentState
.bitrate
= 0;
566 mi
->CurrentState
.samplerate
= 0;
567 mi
->CurrentState
.channels
= 0;
568 mi
->CurrentState
.bits
= 0;
569 mi
->CurrentState
.playlistLength
= 0;
570 mi
->CurrentState
.error
[0] = '\0';
572 mi
->search_type
= MPD_SEARCH_TYPE_NONE
;
573 /* no need to initialize, but set it to anything anyway*/
574 mi
->search_field
= MPD_TAG_ITEM_ARTIST
;
577 if(mi
->output_states
)
578 g_free(mi
->output_states
);
579 mi
->output_states
= NULL
;
582 memset((mi
->supported_tags
), 0,sizeof(mi
->supported_tags
));
586 memcpy(&(mi
->OldState
), &(mi
->CurrentState
) , sizeof(MpdServerState
));
588 mpd_free_queue_ob(mi
);
589 mpd_server_free_commands(mi
);
590 /*don't reset errors */
591 /* Remove this signal, we don't actually disconnect */
594 /* set disconnect flag */
595 mi
->connected
= FALSE
;
597 if(mi
->the_connection_changed_callback
!= NULL
)
599 mi
->the_connection_changed_callback( mi
, FALSE
, mi
->the_connection_changed_signal_userdata
);
602 debug_printf(DEBUG_INFO
, "Disconnect completed\n");
605 int mpd_connect(MpdObj
*mi
)
607 return mpd_connect_real(mi
,NULL
);
609 int mpd_connect_real(MpdObj
*mi
,mpd_Connection
*connection
)
614 /* should return some spiffy error here */
615 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
616 return MPD_ARGS_ERROR
;
620 mi
->error_mpd_code
= 0;
621 if(mi
->error_msg
!= NULL
)
625 mi
->error_msg
= NULL
;
627 debug_printf(DEBUG_INFO
, "connecting\n");
628 mpd_init_MpdServerState(&(mi
->CurrentState
));
630 memcpy(&(mi
->OldState
), &(mi
->CurrentState
), sizeof(MpdServerState
));
638 if(mi
->hostname
== NULL
)
640 mpd_set_hostname(mi
, "localhost");
642 /* make sure this is locked */
643 if(!mi
->connection_lock
)
648 mi
->connection
= connection
;
650 /* make timeout configurable */
651 mi
->connection
= mpd_newConnection(mi
->hostname
,mi
->port
,mi
->connection_timeout
);
653 if(mi
->connection
== NULL
)
655 /* TODO: make seperate error message? */
656 return MPD_NOT_CONNECTED
;
658 if(mpd_check_error(mi
) != MPD_OK
)
660 /* TODO: make seperate error message? */
661 return MPD_NOT_CONNECTED
;
664 /* set connected state */
665 mi
->connected
= TRUE
;
666 if(mpd_unlock_conn(mi
))
668 return MPD_LOCK_FAILED
;
671 /* get the commands we are allowed to use */
672 retv
= mpd_server_get_allowed_commands(mi
);
677 /* Trying to send password, this is needed to get right outputs and tag_types */
678 if(mi
->password
&& strlen(mi
->password
) > 0)
680 mpd_send_password(mi
);
684 /* Update tag types, this is done in send_password.
685 * mpd_send_password() does this.
686 * So only do it when no password is send.
689 char **retv
= mpd_server_get_tag_types(mi
);
691 for(i
=0;i
<MPD_TAG_ITEM_ANY
;i
++)
694 for(j
=0;retv
[j
] && strcasecmp(retv
[j
],mpdTagItemKeys
[i
]); j
++);
695 if(retv
[j
]) mi
->supported_tags
[i
] = TRUE
;
696 else mi
->supported_tags
[i
] = FALSE
;
700 /* also always true */
701 mi
->supported_tags
[MPD_TAG_ITEM_FILENAME
] = TRUE
;
702 mi
->supported_tags
[MPD_TAG_ITEM_ANY
] = TRUE
;
706 retv = mpd_server_update_outputs(mi);
711 retv
= mpd_server_update_outputs(mi
);
712 /** update the supported tags */
713 debug_printf(DEBUG_INFO
, "Propagating connection changed");
715 if(mi
->the_connection_changed_callback
!= NULL
)
717 mi
->the_connection_changed_callback( mi
, TRUE
, mi
->the_connection_changed_signal_userdata
);
723 debug_printf(DEBUG_INFO
, "Connected to mpd");
727 int mpd_check_connected(MpdObj
*mi
)
733 return mi
->connected
;
738 void mpd_signal_connect_status_changed (MpdObj
*mi
, StatusChangedCallback status_changed
, void *userdata
)
742 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
745 mi
->the_status_changed_callback
= status_changed
;
746 mi
->the_status_changed_signal_userdata
= userdata
;
750 void mpd_signal_connect_error(MpdObj
*mi
, ErrorCallback error_callback
, void *userdata
)
754 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
757 mi
->the_error_callback
= error_callback
;
758 mi
->the_error_signal_userdata
= userdata
;
761 void mpd_signal_connect_connection_changed(MpdObj
*mi
, ConnectionChangedCallback connection_changed
, void *userdata
)
765 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
768 mi
->the_connection_changed_callback
= connection_changed
;
769 mi
->the_connection_changed_signal_userdata
= userdata
;
775 MpdData
*mpd_new_data_struct(void)
777 return (MpdData
*) g_slice_new0(MpdData_real
);
780 MpdData
*mpd_new_data_struct_append(MpdData
* data
)
782 MpdData_real
*data_real
= (MpdData_real
*)data
;
783 if(data_real
== NULL
)
785 data_real
= (MpdData_real
*)mpd_new_data_struct();
786 data_real
->first
= data_real
;
790 data_real
->next
= (MpdData_real
*)mpd_new_data_struct();
791 data_real
->next
->prev
= data_real
;
792 data_real
= data_real
->next
;
793 data_real
->next
= NULL
;
794 data_real
->first
= data_real
->prev
->first
;
796 return (MpdData
*)data_real
;
799 MpdData
* mpd_data_get_first(MpdData
const * const data
)
801 MpdData_real
const * const data_real
= (MpdData_real
const * const)data
;
802 if(data_real
!= NULL
)
804 return (MpdData
*)data_real
->first
;
810 MpdData
* mpd_data_get_next(MpdData
* const data
)
812 return mpd_data_get_next_real(data
, TRUE
);
815 MpdData
* mpd_data_get_next_real(MpdData
* const data
, int kill_list
)
817 MpdData_real
*data_real
= (MpdData_real
*)data
;
818 if (data_real
!= NULL
)
820 if (data_real
->next
!= NULL
)
822 return (MpdData
*)data_real
->next
;
826 if (kill_list
) mpd_data_free((MpdData
*)data_real
);
830 return (MpdData
*)data_real
;
833 int mpd_data_is_last(MpdData
const * const data
)
835 MpdData_real
const * const data_real
= (MpdData_real
const * const)data
;
836 if(data_real
!= NULL
)
838 if (data_real
->next
== NULL
)
846 MpdData_head *mpd_data_get_head(MpdData const * const data) {
847 return ((MpdData_real*)data)->head;
850 MpdData
* mpd_data_concatenate( MpdData
* const first
, MpdData
* const second
)
852 MpdData_real
*first_real
= (MpdData_real
*)first
;
853 MpdData_real
*second_real
= (MpdData_real
*)second
;
854 MpdData_real
*first_head
= NULL
;
856 if ( first
== NULL
) {
857 if ( second
!= NULL
)
858 return (MpdData
*)second_real
;
862 if ( second
== NULL
)
863 return (MpdData
*)first_real
;
866 first_head
= (MpdData_real
*)mpd_data_get_first(first
);
868 /* find last element in first data list */
869 while (!mpd_data_is_last((MpdData
*)first_real
)) first_real
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)first_real
, FALSE
);
870 second_real
=(MpdData_real
*) mpd_data_get_first((MpdData
*)second_real
);
872 first_real
->next
= second_real
;
873 second_real
->prev
= first_real
;
875 /* I need to set all the -> first correct */
878 second_real
->first
= first_head
;
879 second_real
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)second_real
, FALSE
);
882 return (MpdData
*)first_head
;
885 * Deletes an item from the list. It returns the next item in the list.
886 * if that is not available, it will return the last item
888 MpdData
* mpd_data_delete_item(MpdData
*data
)
890 MpdData_real
*temp
= NULL
, *data_real
= (MpdData_real
*)data
;
891 if(data_real
== NULL
) return NULL
;
892 /* if there is a next item, fix the prev pointer of the next item */
895 data_real
->next
->prev
= data_real
->prev
;
896 temp
= data_real
->next
;
898 /* if there is a previous item, fix the next pointer of the previous item */
901 /* the next item of the previous is the next item of the current */
902 data_real
->prev
->next
= data_real
->next
;
903 /* temp is the previous item */
904 temp
= data_real
->prev
;
907 /* fix first, if removed item is the first */
908 if(temp
&& temp
->first
== data_real
)
910 MpdData_real
*first
,*node
= temp
;
912 for(;node
->prev
;node
= node
->prev
);
919 /* make the removed row a valid list, so I can use the default free function to free it */
920 data_real
->next
= NULL
;
921 data_real
->prev
= NULL
;
922 data_real
->first
= data_real
;
924 mpd_data_free((MpdData
*)data_real
);
926 return (MpdData
*)temp
;
929 void mpd_data_free(MpdData
*data
)
931 MpdData_real
*data_real
,*temp
;
934 debug_printf(DEBUG_ERROR
, "data != NULL Failed");
937 data_real
= (MpdData_real
*)mpd_data_get_first(data
);
940 if (data_real
->type
== MPD_DATA_TYPE_SONG
) {
941 if(data_real
->song
) mpd_freeSong(data_real
->song
);
942 } else if (data_real
->type
== MPD_DATA_TYPE_OUTPUT_DEV
) {
943 mpd_freeOutputElement(data_real
->output_dev
);
944 } else if(data_real
->type
== MPD_DATA_TYPE_DIRECTORY
) {
945 if(data_real
->directory
)free(data_real
->directory
);
946 } else if(data_real
->type
== MPD_DATA_TYPE_PLAYLIST
) {
947 if(data_real
->playlist
) mpd_freePlaylistFile(data_real
->playlist
);
949 free((void*)(data_real
->tag
));
951 if(data_real
->freefunc
)
953 if(data_real
->userdata
)
954 data_real
->freefunc(data_real
->userdata
);
956 data_real
= data_real
->next
;
957 g_slice_free(MpdData_real
, temp
);
961 /* clean this up.. make one while loop */
962 static void mpd_free_queue_ob(MpdObj
*mi
)
964 MpdQueue
*temp
= NULL
;
967 debug_printf(DEBUG_ERROR
, "mi != NULL failed");
970 if(mi
->queue
== NULL
)
972 debug_printf(DEBUG_INFO
, "mi->queue != NULL failed, nothing to clean.");
975 mi
->queue
= mi
->queue
->first
;
976 while(mi
->queue
!= NULL
)
978 temp
= mi
->queue
->next
;
980 if(mi
->queue
->path
!= NULL
)
982 free(mi
->queue
->path
);
985 g_slice_free(MpdQueue
, mi
->queue
);
992 MpdQueue
*mpd_new_queue_struct()
994 return g_slice_new0(MpdQueue
);
998 void mpd_queue_get_next(MpdObj
*mi
)
1000 if(mi
->queue
!= NULL
&& mi
->queue
->next
!= NULL
)
1002 mi
->queue
= mi
->queue
->next
;
1004 else if(mi
->queue
->next
== NULL
)
1006 mpd_free_queue_ob(mi
);
1011 long unsigned mpd_server_get_database_update_time(MpdObj
*mi
)
1013 if(!mpd_check_connected(mi
))
1015 debug_printf(DEBUG_WARNING
,"not connected\n");
1016 return MPD_NOT_CONNECTED
;
1018 if(mpd_stats_check(mi
) != MPD_OK
)
1020 debug_printf(DEBUG_WARNING
,"Failed grabbing status\n");
1021 return MPD_STATS_FAILED
;
1023 return mi
->stats
->dbUpdateTime
;
1027 MpdData
* mpd_server_get_output_devices(MpdObj
*mi
)
1029 mpd_OutputEntity
*output
= NULL
;
1030 MpdData
*data
= NULL
;
1031 if(!mpd_check_connected(mi
))
1033 debug_printf(DEBUG_WARNING
,"not connected\n");
1036 /* TODO: Check version */
1037 if(mpd_lock_conn(mi
))
1039 debug_printf(DEBUG_ERROR
,"lock failed\n");
1043 mpd_sendOutputsCommand(mi
->connection
);
1044 while (( output
= mpd_getNextOutput(mi
->connection
)) != NULL
)
1046 data
= mpd_new_data_struct_append(data
);
1047 data
->type
= MPD_DATA_TYPE_OUTPUT_DEV
;
1048 data
->output_dev
= output
;
1050 mpd_finishCommand(mi
->connection
);
1053 if(mpd_unlock_conn(mi
) != MPD_OK
)
1055 if(data
)mpd_data_free(data
);
1062 return mpd_data_get_first(data
);
1065 int mpd_server_set_output_device(MpdObj
*mi
,int device_id
,int state
)
1067 if(!mpd_check_connected(mi
))
1069 debug_printf(DEBUG_WARNING
,"not connected\n");
1070 return MPD_NOT_CONNECTED
;
1072 if(mpd_lock_conn(mi
))
1074 debug_printf(DEBUG_ERROR
,"lock failed\n");
1075 return MPD_LOCK_FAILED
;
1079 mpd_sendEnableOutputCommand(mi
->connection
, device_id
);
1083 mpd_sendDisableOutputCommand(mi
->connection
, device_id
);
1085 mpd_finishCommand(mi
->connection
);
1087 mpd_unlock_conn(mi
);
1088 mpd_status_queue_update(mi
);
1092 int mpd_server_check_version(MpdObj
*mi
, int major
, int minor
, int micro
)
1094 if(!mpd_check_connected(mi
))
1096 debug_printf(DEBUG_WARNING
,"not connected\n");
1099 if(major
> mi
->connection
->version
[0]) return FALSE
;
1100 if(mi
->connection
->version
[0] > major
) return TRUE
;
1101 if(minor
> mi
->connection
->version
[1]) return FALSE
;
1102 if(mi
->connection
->version
[1] > minor
) return TRUE
;
1103 if(micro
> mi
->connection
->version
[2]) return FALSE
;
1104 if(mi
->connection
->version
[2] > micro
) return TRUE
;
1108 int mpd_server_check_command_allowed(MpdObj
*mi
, const char *command
)
1111 if(!mi
|| !command
) return MPD_SERVER_COMMAND_ERROR
;
1112 /* when we are connected to a mpd server that doesn't support commands and not commands
1113 * feature. (like mpd 0.11.5) allow everything
1115 if(!mpd_server_check_version(mi
, 0,12,0)) return MPD_SERVER_COMMAND_ALLOWED
;
1117 * Also when somehow we failted to get commands
1119 if(mi
->commands
== NULL
) return MPD_SERVER_COMMAND_ALLOWED
;
1123 for(i
=0;mi
->commands
[i
].command_name
;i
++)
1125 if(!strcasecmp(mi
->commands
[i
].command_name
, command
))
1126 return mi
->commands
[i
].enabled
;
1128 return MPD_SERVER_COMMAND_NOT_SUPPORTED
;
1131 char ** mpd_server_get_url_handlers(MpdObj
*mi
)
1135 if(!mpd_check_connected(mi
))
1137 debug_printf(DEBUG_WARNING
,"not connected\n");
1140 if(mi
->url_handlers
){
1141 return g_strdupv(mi
->url_handlers
);
1143 if(mpd_lock_conn(mi
))
1145 debug_printf(DEBUG_ERROR
,"lock failed\n");
1149 * Fetch url handlers and store them
1151 mpd_sendUrlHandlersCommand(mi
->connection
);
1152 while((temp
= mpd_getNextHandler(mi
->connection
)) != NULL
)
1154 mi
->url_handlers
= realloc(mi
->url_handlers
,(i
+2)*sizeof(*mi
->url_handlers
));
1155 mi
->url_handlers
[i
] = temp
;
1156 mi
->url_handlers
[i
+1] = NULL
;
1159 mpd_finishCommand(mi
->connection
);
1162 mpd_unlock_conn(mi
);
1164 return g_strdupv(mi
->url_handlers
);
1166 char ** mpd_server_get_tag_types(MpdObj
*mi
)
1171 if(!mpd_check_connected(mi
))
1173 debug_printf(DEBUG_WARNING
,"not connected\n");
1176 if(mpd_lock_conn(mi
))
1178 debug_printf(DEBUG_ERROR
,"lock failed\n");
1181 mpd_sendTagTypesCommand(mi
->connection
);
1182 while((temp
= mpd_getNextTagType(mi
->connection
)) != NULL
)
1184 retv
= realloc(retv
,(i
+2)*sizeof(*retv
));
1189 mpd_finishCommand(mi
->connection
);
1192 mpd_unlock_conn(mi
);
1196 int mpd_misc_get_tag_by_name(char *name
)
1201 return MPD_ARGS_ERROR
;
1203 for(i
=0; i
< MPD_TAG_NUM_OF_ITEM_TYPES
; i
++)
1205 if(!strcasecmp(mpdTagItemKeys
[i
], name
))
1210 return MPD_TAG_NOT_FOUND
;
1213 int mpd_server_update_outputs(MpdObj
*mi
)
1215 mpd_OutputEntity
*output
= NULL
;
1216 if(!mpd_check_connected(mi
))
1218 debug_printf(DEBUG_WARNING
,"not connected\n");
1219 return MPD_NOT_CONNECTED
;
1221 if(mpd_lock_conn(mi
))
1223 debug_printf(DEBUG_ERROR
,"lock failed\n");
1224 return MPD_LOCK_FAILED
;
1226 mpd_sendOutputsCommand(mi
->connection
);
1227 while (( output
= mpd_getNextOutput(mi
->connection
)) != NULL
)
1230 mi
->output_states
= realloc(mi
->output_states
,mi
->num_outputs
*sizeof(int));
1231 mi
->output_states
[mi
->num_outputs
-1] = FALSE
;/*output->enabled;*/
1232 mpd_freeOutputElement(output
);
1234 mpd_finishCommand(mi
->connection
);
1235 return mpd_unlock_conn(mi
);
1238 int mpd_server_has_idle(MpdObj
*mi
)
1240 return mi
->has_idle
;
1243 int mpd_server_tag_supported(MpdObj
*mi
, int tag
)
1245 if(!mi
) return FALSE
;
1246 if(tag
< 0 || tag
>= MPD_TAG_NUM_OF_ITEM_TYPES
) {
1249 return mi
->supported_tags
[tag
];
1252 int mpd_server_set_replaygain_mode(MpdObj
*mi
, MpdServerReplaygainMode mode
)
1254 if(!mpd_check_connected(mi
))
1256 debug_printf(DEBUG_WARNING
,"not connected\n");
1257 return MPD_NOT_CONNECTED
;
1259 if(mpd_lock_conn(mi
))
1261 debug_printf(DEBUG_ERROR
,"lock failed\n");
1262 return MPD_LOCK_FAILED
;
1265 case MPD_SERVER_REPLAYGAIN_MODE_AUTO
:
1266 mpd_sendSetReplayGainMode(mi
->connection
, "auto");
1268 case MPD_SERVER_REPLAYGAIN_MODE_TRACK
:
1269 mpd_sendSetReplayGainMode(mi
->connection
, "track");
1271 case MPD_SERVER_REPLAYGAIN_MODE_ALBUM
:
1272 mpd_sendSetReplayGainMode(mi
->connection
, "album");
1275 mpd_sendSetReplayGainMode(mi
->connection
, "off");
1278 mpd_finishCommand(mi
->connection
);
1279 return mpd_unlock_conn(mi
);
1282 MpdServerReplaygainMode
mpd_server_get_replaygain_mode(MpdObj
*mi
)
1285 MpdServerReplaygainMode retv
= MPD_SERVER_REPLAYGAIN_MODE_OFF
;
1286 if(!mpd_check_connected(mi
))
1288 debug_printf(DEBUG_ERROR
, "Not Connected\n");
1292 if(mpd_lock_conn(mi
))
1297 mpd_sendReplayGainModeCommand(mi
->connection
);
1299 var
= mpd_getReplayGainMode(mi
->connection
);
1302 if(strcmp(var
, "track")==0) {
1303 retv
= MPD_SERVER_REPLAYGAIN_MODE_TRACK
;
1304 }else if(strcmp(var
, "album")==0) {
1305 retv
= MPD_SERVER_REPLAYGAIN_MODE_ALBUM
;
1306 }else if (strcmp(var
, "auto") == 0) {
1307 retv
= MPD_SERVER_REPLAYGAIN_MODE_AUTO
;
1311 mpd_finishCommand(mi
->connection
);
1313 mpd_unlock_conn(mi
);