X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fmpdclient.c;h=8c18a0c81ab60044db6df587af04e87abc3d2e46;hb=f1a456c2b13b8885bae41ddaa051447c3742e498;hp=9b896e2691b6613ffa42e680f17be09b8fa701d6;hpb=41ff25c24606d22cb322526742aa6319191518bd;p=ncmpc.git diff --git a/src/mpdclient.c b/src/mpdclient.c index 9b896e2..8c18a0c 100644 --- a/src/mpdclient.c +++ b/src/mpdclient.c @@ -1,63 +1,130 @@ /* ncmpc (Ncurses MPD Client) - * (c) 2004-2009 The Music Player Daemon Project + * (c) 2004-2017 The Music Player Daemon Project * Project homepage: http://musicpd.org - + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - + * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ + */ #include "mpdclient.h" +#include "callbacks.h" #include "filelist.h" -#include "screen_client.h" #include "config.h" -#include "options.h" -#include "strfsong.h" -#include "utils.h" #include "gidle.h" +#include "charset.h" + +#ifdef ENABLE_ASYNC_CONNECT +#include "aconnect.h" +#endif #include -#include -#include -#include -#include +#include -#define BUFSIZE 1024 +static gboolean +mpdclient_enter_idle_callback(gpointer user_data) +{ + struct mpdclient *c = user_data; + assert(c->enter_idle_source_id != 0); + assert(c->source != NULL); + assert(!c->idle); -/* sort by list-format */ -gint -compare_filelistentry_format(gconstpointer filelist_entry1, - gconstpointer filelist_entry2) + c->enter_idle_source_id = 0; + c->idle = mpd_glib_enter(c->source); + return false; +} + +static void +mpdclient_schedule_enter_idle(struct mpdclient *c) { - const struct mpd_entity *e1, *e2; - char key1[BUFSIZE], key2[BUFSIZE]; - int n = 0; + assert(c != NULL); + assert(c->source != NULL); - e1 = ((const struct filelist_entry *)filelist_entry1)->entity; - e2 = ((const struct filelist_entry *)filelist_entry2)->entity; + if (c->enter_idle_source_id == 0) + /* automatically re-enter MPD "idle" mode */ + c->enter_idle_source_id = + g_idle_add(mpdclient_enter_idle_callback, c); +} - if (e1 && e2 && - mpd_entity_get_type(e1) == MPD_ENTITY_TYPE_SONG && - mpd_entity_get_type(e2) == MPD_ENTITY_TYPE_SONG) { - strfsong(key1, BUFSIZE, options.list_format, mpd_entity_get_song(e1)); - strfsong(key2, BUFSIZE, options.list_format, mpd_entity_get_song(e2)); - n = strcmp(key1,key2); +static void +mpdclient_cancel_enter_idle(struct mpdclient *c) +{ + if (c->enter_idle_source_id != 0) { + g_source_remove(c->enter_idle_source_id); + c->enter_idle_source_id = 0; } +} + +static void +mpdclient_invoke_error_callback(enum mpd_error error, + const char *message) +{ + char *allocated; + if (error == MPD_ERROR_SERVER) + /* server errors are UTF-8, the others are locale */ + message = allocated = utf8_to_locale(message); + else + allocated = NULL; + + mpdclient_error_callback(message); + g_free(allocated); +} + +static void +mpdclient_invoke_error_callback1(struct mpdclient *c) +{ + assert(c != NULL); + assert(c->connection != NULL); + + struct mpd_connection *connection = c->connection; + + enum mpd_error error = mpd_connection_get_error(connection); + assert(error != MPD_ERROR_SUCCESS); - return n; + mpdclient_invoke_error_callback(error, + mpd_connection_get_error_message(connection)); } +static void +mpdclient_gidle_callback(enum mpd_error error, + gcc_unused enum mpd_server_error server_error, + const char *message, enum mpd_idle events, + void *ctx) +{ + struct mpdclient *c = ctx; + + c->idle = false; + + assert(mpdclient_is_connected(c)); + + if (error != MPD_ERROR_SUCCESS) { + mpdclient_invoke_error_callback(error, message); + mpdclient_disconnect(c); + mpdclient_lost_callback(); + return; + } + + c->events |= events; + mpdclient_update(c); + + mpdclient_idle_callback(c->events); + + c->events = 0; + + if (c->source != NULL) + mpdclient_schedule_enter_idle(c); +} /****************************************************************************/ /*** mpdclient functions ****************************************************/ @@ -72,33 +139,43 @@ mpdclient_handle_error(struct mpdclient *c) if (error == MPD_ERROR_SERVER && mpd_connection_get_server_error(c->connection) == MPD_SERVER_ERROR_PERMISSION && - screen_auth(c)) + mpdclient_auth_callback(c)) return true; - mpdclient_ui_error(mpd_connection_get_error_message(c->connection)); + mpdclient_invoke_error_callback(error, + mpd_connection_get_error_message(c->connection)); - if (!mpd_connection_clear_error(c->connection)) + if (!mpd_connection_clear_error(c->connection)) { mpdclient_disconnect(c); + mpdclient_lost_callback(); + } return false; } -static bool -mpdclient_finish_command(struct mpdclient *c) -{ - return mpd_response_finish(c->connection) - ? true : mpdclient_handle_error(c); -} - struct mpdclient * -mpdclient_new(void) +mpdclient_new(const gchar *host, unsigned port, + unsigned timeout_ms, const gchar *password) { - struct mpdclient *c; + struct mpdclient *c = g_new0(struct mpdclient, 1); + +#ifdef ENABLE_ASYNC_CONNECT + c->settings = mpd_settings_new(host, port, timeout_ms, + NULL, NULL); + if (c->settings == NULL) + g_error("Out of memory"); +#else + c->host = host; + c->port = port; +#endif + + c->timeout_ms = timeout_ms; + c->password = password; - c = g_new0(struct mpdclient, 1); playlist_init(&c->playlist); c->volume = -1; c->events = 0; + c->playing = false; return c; } @@ -110,25 +187,89 @@ mpdclient_free(struct mpdclient *c) mpdclient_playlist_free(&c->playlist); +#ifdef ENABLE_ASYNC_CONNECT + mpd_settings_free(c->settings); +#endif + g_free(c); } +static char * +settings_name(const struct mpd_settings *settings) +{ + assert(settings != NULL); + + const char *host = mpd_settings_get_host(settings); + if (host == NULL) + host = "unknown"; + + if (host[0] == '/') + return g_strdup(host); + + unsigned port = mpd_settings_get_port(settings); + if (port == 0 || port == 6600) + return g_strdup(host); + + return g_strdup_printf("%s:%u", host, port); +} + +char * +mpdclient_settings_name(const struct mpdclient *c) +{ + assert(c != NULL); + +#ifdef ENABLE_ASYNC_CONNECT + return settings_name(c->settings); +#else + struct mpd_settings *settings = + mpd_settings_new(c->host, c->port, 0, NULL, NULL); + if (settings == NULL) + return g_strdup("unknown"); + + char *name = settings_name(settings); + mpd_settings_free(settings); + return name; +#endif +} + +static void +mpdclient_status_free(struct mpdclient *c) +{ + if (c->status == NULL) + return; + + mpd_status_free(c->status); + c->status = NULL; + + c->volume = -1; + c->playing = false; +} + void mpdclient_disconnect(struct mpdclient *c) { +#ifdef ENABLE_ASYNC_CONNECT + if (c->async_connect != NULL) { + aconnect_cancel(c->async_connect); + c->async_connect = NULL; + } +#endif + + mpdclient_cancel_enter_idle(c); + if (c->source != NULL) { mpd_glib_free(c->source); c->source = NULL; c->idle = false; } - if (c->connection) + if (c->connection) { mpd_connection_free(c->connection); + ++c->connection_id; + } c->connection = NULL; - if (c->status) - mpd_status_free(c->status); - c->status = NULL; + mpdclient_status_free(c); playlist_clear(&c->playlist); @@ -136,103 +277,136 @@ mpdclient_disconnect(struct mpdclient *c) c->song = NULL; /* everything has changed after a disconnect */ - c->events |= MPD_IDLE_DATABASE|MPD_IDLE_STORED_PLAYLIST| - MPD_IDLE_QUEUE|MPD_IDLE_PLAYER|MPD_IDLE_MIXER|MPD_IDLE_OUTPUT| - MPD_IDLE_OPTIONS|MPD_IDLE_UPDATE; + c->events |= MPD_IDLE_ALL; } -bool -mpdclient_connect(struct mpdclient *c, - const gchar *host, - gint port, - gfloat _timeout, - const gchar *password) +static bool +mpdclient_connected(struct mpdclient *c, + struct mpd_connection *connection) { - /* close any open connection */ - if( c->connection ) - mpdclient_disconnect(c); - - /* connect to MPD */ - c->connection = mpd_connection_new(host, port, _timeout * 1000); - if (c->connection == NULL) - g_error("Out of memory"); + c->connection = connection; - if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) { - mpdclient_handle_error(c); + if (mpd_connection_get_error(connection) != MPD_ERROR_SUCCESS) { + mpdclient_invoke_error_callback1(c); mpdclient_disconnect(c); + mpdclient_failed_callback(); return false; } +#ifdef ENABLE_ASYNC_CONNECT + if (c->timeout_ms > 0) + mpd_connection_set_timeout(connection, c->timeout_ms); +#endif + /* send password */ - if (password != NULL && !mpd_run_password(c->connection, password)) { - mpdclient_handle_error(c); + if (c->password != NULL && + !mpd_run_password(connection, c->password)) { + mpdclient_invoke_error_callback1(c); mpdclient_disconnect(c); + mpdclient_failed_callback(); return false; } + c->source = mpd_glib_new(connection, + mpdclient_gidle_callback, c); + mpdclient_schedule_enter_idle(c); + + ++c->connection_id; + + mpdclient_connected_callback(); return true; } +#ifdef ENABLE_ASYNC_CONNECT + +static void +mpdclient_connect_success(struct mpd_connection *connection, void *ctx) +{ + struct mpdclient *c = ctx; + assert(c->async_connect != NULL); + c->async_connect = NULL; + + mpdclient_connected(c, connection); +} + +static void +mpdclient_connect_error(const char *message, void *ctx) +{ + struct mpdclient *c = ctx; + assert(c->async_connect != NULL); + c->async_connect = NULL; + + mpdclient_error_callback(message); + mpdclient_failed_callback(); +} + +static const struct aconnect_handler mpdclient_connect_handler = { + .success = mpdclient_connect_success, + .error = mpdclient_connect_error, +}; + +#endif + +void +mpdclient_connect(struct mpdclient *c) +{ + /* close any open connection */ + mpdclient_disconnect(c); + +#ifdef ENABLE_ASYNC_CONNECT + aconnect_start(&c->async_connect, + mpd_settings_get_host(c->settings), + mpd_settings_get_port(c->settings), + &mpdclient_connect_handler, c); +#else + /* connect to MPD */ + struct mpd_connection *connection = + mpd_connection_new(c->host, c->port, c->timeout_ms); + if (connection == NULL) + g_error("Out of memory"); + + mpdclient_connected(c, connection); +#endif +} + bool mpdclient_update(struct mpdclient *c) { struct mpd_connection *connection = mpdclient_get_connection(c); - bool retval; - - c->volume = -1; if (connection == NULL) return false; - /* always announce these options as long as we don't have - "idle" support */ - if (c->source == NULL) - c->events |= MPD_IDLE_PLAYER|MPD_IDLE_OPTIONS; - /* free the old status */ - if (c->status) - mpd_status_free(c->status); + mpdclient_status_free(c); /* retrieve new status */ c->status = mpd_run_status(connection); if (c->status == NULL) return mpdclient_handle_error(c); - if (c->source == NULL && - c->update_id != mpd_status_get_update_id(c->status)) { - c->events |= MPD_IDLE_UPDATE; - - if (c->update_id > 0) - c->events |= MPD_IDLE_DATABASE; - } - - c->update_id = mpd_status_get_update_id(c->status); - - if (c->source == NULL && - c->volume != mpd_status_get_volume(c->status)) - c->events |= MPD_IDLE_MIXER; - c->volume = mpd_status_get_volume(c->status); + c->playing = mpd_status_get_state(c->status) == MPD_STATE_PLAY; /* check if the playlist needs an update */ if (c->playlist.version != mpd_status_get_queue_version(c->status)) { - if (c->source == NULL) - c->events |= MPD_IDLE_PLAYLIST; + bool retval; if (!playlist_is_empty(&c->playlist)) retval = mpdclient_playlist_update_changes(c); else retval = mpdclient_playlist_update(c); - } else - retval = true; + if (!retval) + return false; + } /* update the current song */ - if (!c->song || mpd_status_get_song_id(c->status)) { + if (!c->song || mpd_status_get_song_id(c->status) >= 0) { c->song = playlist_get_song(&c->playlist, mpd_status_get_song_pos(c->status)); } - return retval; + return true; } struct mpd_connection * @@ -241,74 +415,54 @@ mpdclient_get_connection(struct mpdclient *c) if (c->source != NULL && c->idle) { c->idle = false; mpd_glib_leave(c->source); + + mpdclient_schedule_enter_idle(c); } return c->connection; } -void -mpdclient_put_connection(struct mpdclient *c) +static struct mpd_status * +mpdclient_recv_status(struct mpdclient *c) { - assert(c->source == NULL || c->connection != NULL); + assert(c->connection != NULL); - if (c->source != NULL && !c->idle) { - c->idle = true; - mpd_glib_enter(c->source); + struct mpd_status *status = mpd_recv_status(c->connection); + if (status == NULL) { + mpdclient_handle_error(c); + return NULL; } -} + if (c->status != NULL) + mpd_status_free(c->status); + return c->status = status; +} /****************************************************************************/ /*** MPD Commands **********************************************************/ /****************************************************************************/ bool -mpdclient_cmd_play(struct mpdclient *c, gint idx) +mpdclient_cmd_crop(struct mpdclient *c) { - struct mpd_connection *connection = mpdclient_get_connection(c); - const struct mpd_song *song = playlist_get_song(&c->playlist, idx); - - if (connection == NULL) + if (!mpdclient_is_playing(c)) return false; - if (song) - mpd_send_play_id(connection, mpd_song_get_id(song)); - else - mpd_send_play(connection); - - return mpdclient_finish_command(c); -} + int length = mpd_status_get_queue_length(c->status); + int current = mpd_status_get_song_pos(c->status); + if (current < 0 || mpd_status_get_queue_length(c->status) < 2) + return true; -bool -mpdclient_cmd_crop(struct mpdclient *c) -{ struct mpd_connection *connection = mpdclient_get_connection(c); - struct mpd_status *status; - bool playing; - int length, current; - if (connection == NULL) return false; - status = mpd_run_status(connection); - if (status == NULL) - return mpdclient_handle_error(c); - - playing = mpd_status_get_state(status) == MPD_STATE_PLAY || - mpd_status_get_state(status) == MPD_STATE_PAUSE; - length = mpd_status_get_queue_length(status); - current = mpd_status_get_song_pos(status); - - mpd_status_free(status); - - if (!playing || length < 2) - return true; - mpd_command_list_begin(connection, false); - while (--length >= 0) - if (length != current) - mpd_send_delete(connection, length); + if (current < length - 1) + mpd_send_delete_range(connection, current + 1, length); + if (current > 0) + mpd_send_delete_range(connection, 0, current); mpd_command_list_end(connection); @@ -319,18 +473,38 @@ bool mpdclient_cmd_clear(struct mpdclient *c) { struct mpd_connection *connection = mpdclient_get_connection(c); - bool retval; - if (connection == NULL) return false; - mpd_send_clear(connection); - retval = mpdclient_finish_command(c); + /* send "clear" and "status" */ + if (!mpd_command_list_begin(connection, false) || + !mpd_send_clear(connection) || + !mpd_send_status(connection) || + !mpd_command_list_end(connection)) + return mpdclient_handle_error(c); - if (retval) - c->events |= MPD_IDLE_PLAYLIST; + /* receive the new status, store it in the mpdclient struct */ - return retval; + struct mpd_status *status = mpdclient_recv_status(c); + if (status == NULL) + return false; + + if (!mpd_response_finish(connection)) + return mpdclient_handle_error(c); + + /* update mpdclient.playlist */ + + if (mpd_status_get_queue_length(status) == 0) { + /* after the "clear" command, the queue is really + empty - this means we can clear it locally, + reducing the UI latency */ + playlist_clear(&c->playlist); + c->playlist.version = mpd_status_get_queue_version(status); + c->song = NULL; + } + + c->events |= MPD_IDLE_QUEUE; + return true; } bool @@ -347,39 +521,26 @@ mpdclient_cmd_volume(struct mpdclient *c, gint value) bool mpdclient_cmd_volume_up(struct mpdclient *c) { + if (c->volume < 0 || c->volume >= 100) + return true; + struct mpd_connection *connection = mpdclient_get_connection(c); if (connection == NULL) return false; - if (c->status == NULL || - mpd_status_get_volume(c->status) == -1) - return true; - - if (c->volume < 0) - c->volume = mpd_status_get_volume(c->status); - - if (c->volume >= 100) - return true; - return mpdclient_cmd_volume(c, ++c->volume); } bool mpdclient_cmd_volume_down(struct mpdclient *c) { + if (c->volume <= 0) + return true; + struct mpd_connection *connection = mpdclient_get_connection(c); if (connection == NULL) return false; - if (c->status == NULL || mpd_status_get_volume(c->status) < 0) - return true; - - if (c->volume < 0) - c->volume = mpd_status_get_volume(c->status); - - if (c->volume <= 0) - return true; - return mpdclient_cmd_volume(c, --c->volume); } @@ -390,20 +551,17 @@ mpdclient_cmd_add_path(struct mpdclient *c, const gchar *path_utf8) if (connection == NULL) return false; - mpd_send_add(connection, path_utf8); - return mpdclient_finish_command(c); + return mpd_send_add(connection, path_utf8)? + mpdclient_finish_command(c) : false; } bool mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song) { - struct mpd_connection *connection = mpdclient_get_connection(c); - struct mpd_status *status; - struct mpd_song *new_song; - assert(c != NULL); assert(song != NULL); + struct mpd_connection *connection = mpdclient_get_connection(c); if (connection == NULL || c->status == NULL) return false; @@ -420,19 +578,16 @@ mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song) !mpd_response_next(connection)) return mpdclient_handle_error(c); - c->events |= MPD_IDLE_PLAYLIST; + c->events |= MPD_IDLE_QUEUE; - status = mpd_recv_status(connection); - if (status != NULL) { - if (c->status != NULL) - mpd_status_free(c->status); - c->status = status; - } + struct mpd_status *status = mpdclient_recv_status(c); + if (status == NULL) + return false; if (!mpd_response_next(connection)) return mpdclient_handle_error(c); - new_song = mpd_recv_song(connection); + struct mpd_song *new_song = mpd_recv_song(connection); if (!mpd_response_finish(connection) || new_song == NULL) { if (new_song != NULL) mpd_song_free(new_song); @@ -462,8 +617,6 @@ bool mpdclient_cmd_delete(struct mpdclient *c, gint idx) { struct mpd_connection *connection = mpdclient_get_connection(c); - const struct mpd_song *song; - struct mpd_status *status; if (connection == NULL || c->status == NULL) return false; @@ -471,7 +624,7 @@ mpdclient_cmd_delete(struct mpdclient *c, gint idx) if (idx < 0 || (guint)idx >= playlist_length(&c->playlist)) return false; - song = playlist_get(&c->playlist, idx); + const struct mpd_song *song = playlist_get(&c->playlist, idx); /* send the delete command to mpd; at the same time, get the new status (to verify the playlist id) */ @@ -482,14 +635,11 @@ mpdclient_cmd_delete(struct mpdclient *c, gint idx) !mpd_command_list_end(connection)) return mpdclient_handle_error(c); - c->events |= MPD_IDLE_PLAYLIST; + c->events |= MPD_IDLE_QUEUE; - status = mpd_recv_status(connection); - if (status != NULL) { - if (c->status != NULL) - mpd_status_free(c->status); - c->status = status; - } + struct mpd_status *status = mpdclient_recv_status(c); + if (status == NULL) + return false; if (!mpd_response_finish(connection)) return mpdclient_handle_error(c); @@ -512,47 +662,18 @@ mpdclient_cmd_delete(struct mpdclient *c, gint idx) return true; } -/** - * Fallback for mpdclient_cmd_delete_range() on MPD older than 0.16. - * It emulates the "delete range" command with a list of simple - * "delete" commands. - */ -static bool -mpdclient_cmd_delete_range_fallback(struct mpdclient *c, - unsigned start, unsigned end) -{ - struct mpd_connection *connection = mpdclient_get_connection(c); - if (connection == NULL) - return false; - - if (!mpd_command_list_begin(connection, false)) - return mpdclient_handle_error(c); - - for (; start < end; --end) - mpd_send_delete(connection, start); - - if (!mpd_command_list_end(connection) || - !mpd_response_finish(connection)) - return mpdclient_handle_error(c); - - return true; -} - bool mpdclient_cmd_delete_range(struct mpdclient *c, unsigned start, unsigned end) { - struct mpd_connection *connection; - struct mpd_status *status; + if (end == start + 1) + /* if that's not really a range, we choose to use the + safer "deleteid" version */ + return mpdclient_cmd_delete(c, start); - connection = mpdclient_get_connection(c); + struct mpd_connection *connection = mpdclient_get_connection(c); if (connection == NULL) return false; - if (mpd_connection_cmp_server_version(connection, 0, 16, 0) < 0) - return mpdclient_cmd_delete_range_fallback(c, start, end); - - /* MPD 0.16 supports "delete" with a range argument */ - /* send the delete command to mpd; at the same time, get the new status (to verify the playlist id) */ @@ -562,14 +683,11 @@ mpdclient_cmd_delete_range(struct mpdclient *c, unsigned start, unsigned end) !mpd_command_list_end(connection)) return mpdclient_handle_error(c); - c->events |= MPD_IDLE_PLAYLIST; + c->events |= MPD_IDLE_QUEUE; - status = mpd_recv_status(connection); - if (status != NULL) { - if (c->status != NULL) - mpd_status_free(c->status); - c->status = status; - } + struct mpd_status *status = mpdclient_recv_status(c); + if (status == NULL) + return false; if (!mpd_response_finish(connection)) return mpdclient_handle_error(c); @@ -597,40 +715,29 @@ mpdclient_cmd_delete_range(struct mpdclient *c, unsigned start, unsigned end) } bool -mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index) +mpdclient_cmd_move(struct mpdclient *c, unsigned dest_pos, unsigned src_pos) { - struct mpd_connection *connection = mpdclient_get_connection(c); - const struct mpd_song *song1, *song2; - struct mpd_status *status; + if (dest_pos == src_pos) + return true; + struct mpd_connection *connection = mpdclient_get_connection(c); if (connection == NULL) return false; - if (old_index == new_index || new_index < 0 || - (guint)new_index >= c->playlist.list->len) - return false; - - song1 = playlist_get(&c->playlist, old_index); - song2 = playlist_get(&c->playlist, new_index); - - /* send the delete command to mpd; at the same time, get the + /* send the "move" command to MPD; at the same time, get the new status (to verify the playlist id) */ if (!mpd_command_list_begin(connection, false) || - !mpd_send_swap_id(connection, mpd_song_get_id(song1), - mpd_song_get_id(song2)) || + !mpd_send_move(connection, src_pos, dest_pos) || !mpd_send_status(connection) || !mpd_command_list_end(connection)) return mpdclient_handle_error(c); - c->events |= MPD_IDLE_PLAYLIST; + c->events |= MPD_IDLE_QUEUE; - status = mpd_recv_status(connection); - if (status != NULL) { - if (c->status != NULL) - mpd_status_free(c->status); - c->status = status; - } + struct mpd_status *status = mpdclient_recv_status(c); + if (status == NULL) + return false; if (!mpd_response_finish(connection)) return mpdclient_handle_error(c); @@ -643,12 +750,80 @@ mpdclient_cmd_move(struct mpdclient *c, gint old_index, gint new_index) c->playlist.version = mpd_status_get_queue_version(status); /* swap songs in the local playlist */ - playlist_swap(&c->playlist, old_index, new_index); + playlist_move(&c->playlist, dest_pos, src_pos); } return true; } +/* The client-to-client protocol (MPD 0.17.0) */ + +bool +mpdclient_cmd_subscribe(struct mpdclient *c, const char *channel) +{ + struct mpd_connection *connection = mpdclient_get_connection(c); + + if (connection == NULL) + return false; + + if (!mpd_send_subscribe(connection, channel)) + return mpdclient_handle_error(c); + + return mpdclient_finish_command(c); +} + +bool +mpdclient_cmd_unsubscribe(struct mpdclient *c, const char *channel) +{ + struct mpd_connection *connection = mpdclient_get_connection(c); + if (connection == NULL) + return false; + + if (!mpd_send_unsubscribe(connection, channel)) + return mpdclient_handle_error(c); + + return mpdclient_finish_command(c); +} + +bool +mpdclient_cmd_send_message(struct mpdclient *c, const char *channel, + const char *text) +{ + struct mpd_connection *connection = mpdclient_get_connection(c); + if (connection == NULL) + return false; + + if (!mpd_send_send_message(connection, channel, text)) + return mpdclient_handle_error(c); + + return mpdclient_finish_command(c); +} + +bool +mpdclient_send_read_messages(struct mpdclient *c) +{ + struct mpd_connection *connection = mpdclient_get_connection(c); + if (connection == NULL) + return false; + + return mpd_send_read_messages(connection)? + true : mpdclient_handle_error(c); +} + +struct mpd_message * +mpdclient_recv_message(struct mpdclient *c) +{ + struct mpd_connection *connection = mpdclient_get_connection(c); + if (connection == NULL) + return false; + + struct mpd_message *message = mpd_recv_message(connection); + if (message == NULL && + mpd_connection_get_error(connection) != MPD_ERROR_SUCCESS) + mpdclient_handle_error(c); + + return message; +} /****************************************************************************/ /*** Playlist management functions ******************************************/ @@ -659,14 +834,14 @@ bool mpdclient_playlist_update(struct mpdclient *c) { struct mpd_connection *connection = mpdclient_get_connection(c); - struct mpd_entity *entity; - if (connection == NULL) return false; playlist_clear(&c->playlist); mpd_send_list_queue_meta(connection); + + struct mpd_entity *entity; while ((entity = mpd_recv_entity(connection))) { if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) playlist_append(&c->playlist, mpd_entity_get_song(entity)); @@ -685,14 +860,13 @@ bool mpdclient_playlist_update_changes(struct mpdclient *c) { struct mpd_connection *connection = mpdclient_get_connection(c); - struct mpd_song *song; - guint length; if (connection == NULL) return false; mpd_send_queue_changes_meta(connection, c->playlist.version); + struct mpd_song *song; while ((song = mpd_recv_song(connection)) != NULL) { int pos = mpd_song_get_pos(song); @@ -709,7 +883,7 @@ mpdclient_playlist_update_changes(struct mpdclient *c) /* remove trailing songs */ - length = mpd_status_get_queue_length(c->status); + unsigned length = mpd_status_get_queue_length(c->status); while (length < c->playlist.list->len) { guint pos = c->playlist.list->len - 1; @@ -732,8 +906,6 @@ bool mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl) { struct mpd_connection *connection = mpdclient_get_connection(c); - guint i; - if (connection == NULL) return false; @@ -742,7 +914,7 @@ mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl) mpd_command_list_begin(connection, false); - for (i = 0; i < filelist_length(fl); ++i) { + for (unsigned i = 0; i < filelist_length(fl); ++i) { struct filelist_entry *entry = filelist_get(fl, i); struct mpd_entity *entity = entry->entity; @@ -758,61 +930,3 @@ mpdclient_filelist_add_all(struct mpdclient *c, struct filelist *fl) mpd_command_list_end(connection); return mpdclient_finish_command(c); } - -static GList * -recv_tag_values(struct mpd_connection *connection, enum mpd_tag_type tag) -{ - GList *list = NULL; - struct mpd_pair *pair; - - while ((pair = mpd_recv_pair_tag(connection, tag)) != NULL) { - list = g_list_append(list, g_strdup(pair->value)); - mpd_return_pair(connection, pair); - } - - return list; -} - -GList * -mpdclient_get_artists(struct mpdclient *c) -{ - struct mpd_connection *connection = mpdclient_get_connection(c); - GList *list; - - if (connection == NULL) - return NULL; - - mpd_search_db_tags(connection, MPD_TAG_ARTIST); - mpd_search_commit(connection); - - list = recv_tag_values(connection, MPD_TAG_ARTIST); - - if (!mpdclient_finish_command(c)) - return string_list_free(list); - - return list; -} - -GList * -mpdclient_get_albums(struct mpdclient *c, const gchar *artist_utf8) -{ - struct mpd_connection *connection = mpdclient_get_connection(c); - GList *list; - - if (connection == NULL) - return NULL; - - mpd_search_db_tags(connection, MPD_TAG_ALBUM); - if (artist_utf8 != NULL) - mpd_search_add_tag_constraint(connection, - MPD_OPERATOR_DEFAULT, - MPD_TAG_ARTIST, artist_utf8); - mpd_search_commit(connection); - - list = recv_tag_values(connection, MPD_TAG_ALBUM); - - if (!mpdclient_finish_command(c)) - return string_list_free(list); - - return list; -}