diff --git a/src/mpdclient.c b/src/mpdclient.c
index 2d6598518086d5cd080d13aa949207a33a77a7aa..d60436946a378753538c1711cb653d0ce4bc9c16 100644 (file)
--- a/src/mpdclient.c
+++ b/src/mpdclient.c
#include <time.h>
#include <string.h>
-#undef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD /* broken with song id's */
-#define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
#define ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE
#define BUFSIZE 1024
if (error == MPD_ERROR_SERVER)
error = error | (mpd_connection_get_server_error(c->connection) << 8);
- for (GList *list = c->error_callbacks; list != NULL;
- list = list->next) {
- mpdc_error_cb_t cb = list->data;
- cb(c, error, mpd_connection_get_error_message(c->connection));
- }
+ mpdclient_ui_error(mpd_connection_get_error_message(c->connection));
if (!mpd_connection_clear_error(c->connection))
mpdclient_disconnect(c);
c = g_new0(struct mpdclient, 1);
playlist_init(&c->playlist);
c->volume = -1;
+ c->events = 0;
return c;
}
mpdclient_playlist_free(&c->playlist);
- g_list_free(c->error_callbacks);
- g_list_free(c->playlist_callbacks);
- g_list_free(c->browse_callbacks);
g_free(c);
}
if (MPD_ERROR(c))
return false;
+ /* always announce these options as long as we don't have real
+ "idle" support */
+ c->events |= MPD_IDLE_PLAYER|MPD_IDLE_OPTIONS;
+
/* free the old status */
if (c->status)
mpd_status_free(c->status);
if (c->status == NULL)
return mpdclient_handle_error(c) == 0;
- if (c->update_id > 0 &&
- c->update_id != mpd_status_get_update_id(c->status))
- mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL);
+ if (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->volume != mpd_status_get_volume(c->status))
+ c->events |= MPD_IDLE_MIXER;
+
c->volume = mpd_status_get_volume(c->status);
/* check if the playlist needs an update */
- if (c->playlist.id != mpd_status_get_queue_version(c->status)) {
+ if (c->playlist.version != mpd_status_get_queue_version(c->status)) {
+ c->events |= MPD_IDLE_PLAYLIST;
+
if (!playlist_is_empty(&c->playlist))
retval = mpdclient_playlist_update_changes(c);
else
mpd_send_clear(c->connection);
retval = mpdclient_finish_command(c);
- /* call playlist updated callback */
- mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL);
+
+ if (retval)
+ c->events |= MPD_IDLE_PLAYLIST;
+
return retval;
}
gint
mpdclient_cmd_add(struct mpdclient *c, const struct mpd_song *song)
{
- gint retval = 0;
+ struct mpd_status *status;
+ struct mpd_song *new_song;
- if (MPD_ERROR(c))
- return -1;
+ assert(c != NULL);
+ assert(song != NULL);
- if (song == NULL)
+ if (MPD_ERROR(c) || c->status == NULL)
return -1;
- /* send the add command to mpd */
- mpd_send_add(c->connection, mpd_song_get_uri(song));
- if( (retval=mpdclient_finish_command(c)) )
- return retval;
+ /* send the add command to mpd; at the same time, get the new
+ status (to verify the new playlist id) and the last song
+ (we hope that's the song we just added) */
+
+ if (!mpd_command_list_begin(c->connection, true) ||
+ !mpd_send_add(c->connection, mpd_song_get_uri(song)) ||
+ !mpd_send_status(c->connection) ||
+ !mpd_send_get_queue_song_pos(c->connection,
+ playlist_length(&c->playlist)) ||
+ !mpd_command_list_end(c->connection) ||
+ !mpd_response_next(c->connection))
+ return mpdclient_handle_error(c);
-#ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD
- /* add the song to playlist */
- playlist_append(&c->playlist, song);
+ c->events |= MPD_IDLE_PLAYLIST;
- /* increment the playlist id, so we don't retrieve a new playlist */
- c->playlist.id++;
+ status = mpd_recv_status(c->connection);
+ if (status != NULL) {
+ if (c->status != NULL)
+ mpd_status_free(c->status);
+ c->status = status;
+ }
- /* call playlist updated callback */
- mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song);
-#endif
+ if (!mpd_response_next(c->connection))
+ return mpdclient_handle_error(c);
- return 0;
+ new_song = mpd_recv_song(c->connection);
+ if (!mpd_response_finish(c->connection) || new_song == NULL) {
+ if (new_song != NULL)
+ mpd_song_free(new_song);
+
+ return mpd_connection_clear_error(c->connection)
+ ? 0 : mpdclient_handle_error(c);
+ }
+
+ if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) + 1 &&
+ mpd_status_get_queue_version(status) == c->playlist.version + 1) {
+ /* the cheap route: match on the new playlist length
+ and its version, we can keep our local playlist
+ copy in sync */
+ c->playlist.version = mpd_status_get_queue_version(status);
+
+ /* the song we just received has the correct id;
+ append it to the local playlist */
+ playlist_append(&c->playlist, new_song);
+ }
+
+ mpd_song_free(new_song);
+
+ return -0;
}
gint
mpdclient_cmd_delete(struct mpdclient *c, gint idx)
{
- gint retval = 0;
- struct mpd_song *song;
+ const struct mpd_song *song;
+ struct mpd_status *status;
- if (MPD_ERROR(c))
+ if (MPD_ERROR(c) || c->status == NULL)
return -1;
if (idx < 0 || (guint)idx >= playlist_length(&c->playlist))
song = playlist_get(&c->playlist, idx);
- /* send the delete command to mpd */
- mpd_send_delete_id(c->connection, mpd_song_get_id(song));
- if( (retval=mpdclient_finish_command(c)) )
- return retval;
+ /* send the delete command to mpd; at the same time, get the
+ new status (to verify the playlist id) */
-#ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE
- /* increment the playlist id, so we don't retrieve a new playlist */
- c->playlist.id++;
+ if (!mpd_command_list_begin(c->connection, false) ||
+ !mpd_send_delete_id(c->connection, mpd_song_get_id(song)) ||
+ !mpd_send_status(c->connection) ||
+ !mpd_command_list_end(c->connection))
+ return mpdclient_handle_error(c);
- /* remove the song from the playlist */
- playlist_remove_reuse(&c->playlist, idx);
+ c->events |= MPD_IDLE_PLAYLIST;
- /* call playlist updated callback */
- mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song);
+ status = mpd_recv_status(c->connection);
+ if (status != NULL) {
+ if (c->status != NULL)
+ mpd_status_free(c->status);
+ c->status = status;
+ }
- /* remove references to the song */
- if (c->song == song)
- c->song = NULL;
+ if (!mpd_response_finish(c->connection))
+ return mpdclient_handle_error(c);
- mpd_song_free(song);
-#endif
+ if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - 1 &&
+ mpd_status_get_queue_version(status) == c->playlist.version + 1) {
+ /* the cheap route: match on the new playlist length
+ and its version, we can keep our local playlist
+ copy in sync */
+ c->playlist.version = mpd_status_get_queue_version(status);
+
+ /* remove the song from the local playlist */
+ playlist_remove(&c->playlist, idx);
+
+ /* remove references to the song */
+ if (c->song == song)
+ c->song = NULL;
+ }
+
+ return 0;
+}
+
+/**
+ * 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 gint
+mpdclient_cmd_delete_range_fallback(struct mpdclient *c,
+ unsigned start, unsigned end)
+{
+ if (!mpd_command_list_begin(c->connection, false))
+ return mpdclient_handle_error(c);
+
+ for (; start < end; --end)
+ mpd_send_delete(c->connection, start);
+
+ if (!mpd_command_list_end(c->connection) ||
+ !mpd_response_finish(c->connection))
+ return mpdclient_handle_error(c);
+
+ return 0;
+}
+
+gint
+mpdclient_cmd_delete_range(struct mpdclient *c, unsigned start, unsigned end)
+{
+ struct mpd_status *status;
+
+ if (MPD_ERROR(c))
+ return -1;
+
+ if (mpd_connection_cmp_server_version(c->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) */
+
+ if (!mpd_command_list_begin(c->connection, false) ||
+ !mpd_send_delete_range(c->connection, start, end) ||
+ !mpd_send_status(c->connection) ||
+ !mpd_command_list_end(c->connection))
+ return mpdclient_handle_error(c);
+
+ c->events |= MPD_IDLE_PLAYLIST;
+
+ status = mpd_recv_status(c->connection);
+ if (status != NULL) {
+ if (c->status != NULL)
+ mpd_status_free(c->status);
+ c->status = status;
+ }
+
+ if (!mpd_response_finish(c->connection))
+ return mpdclient_handle_error(c);
+
+ if (mpd_status_get_queue_length(status) == playlist_length(&c->playlist) - (end - start) &&
+ mpd_status_get_queue_version(status) == c->playlist.version + 1) {
+ /* the cheap route: match on the new playlist length
+ and its version, we can keep our local playlist
+ copy in sync */
+ c->playlist.version = mpd_status_get_queue_version(status);
+
+ /* remove the song from the local playlist */
+ while (end > start) {
+ --end;
+
+ /* remove references to the song */
+ if (c->song == playlist_get(&c->playlist, end))
+ c->song = NULL;
+
+ playlist_remove(&c->playlist, end);
+ }
+ }
return 0;
}
playlist_swap(&c->playlist, old_index, new_index);
/* increment the playlist id, so we don't retrieve a new playlist */
- c->playlist.id++;
+ c->playlist.version++;
#endif
- /* call playlist updated callback */
- mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index);
+ c->events |= MPD_IDLE_PLAYLIST;
return 0;
}
return -1;
mpd_send_save(c->connection, filename_utf8);
- if ((retval = mpdclient_finish_command(c)) == 0)
- mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL);
+ if ((retval = mpdclient_finish_command(c)) == 0) {
+ c->events |= MPD_IDLE_STORED_PLAYLIST;
+ }
+
return retval;
}
mpd_send_rm(c->connection, filename_utf8);
if ((retval = mpdclient_finish_command(c)) == 0)
- mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL);
- return retval;
-}
-
-
-/****************************************************************************/
-/*** Callback management functions ******************************************/
-/****************************************************************************/
-
-static void
-do_list_callbacks(struct mpdclient *c, GList *list, gint event, gpointer data)
-{
- while (list) {
- mpdc_list_cb_t fn = list->data;
-
- fn(c, event, data);
- list = list->next;
- }
-}
-
-void
-mpdclient_playlist_callback(struct mpdclient *c, int event, gpointer data)
-{
- do_list_callbacks(c, c->playlist_callbacks, event, data);
-}
+ c->events |= MPD_IDLE_STORED_PLAYLIST;
-void
-mpdclient_install_playlist_callback(struct mpdclient *c,mpdc_list_cb_t cb)
-{
- c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb);
-}
-
-void
-mpdclient_remove_playlist_callback(struct mpdclient *c, mpdc_list_cb_t cb)
-{
- c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb);
-}
-
-void
-mpdclient_browse_callback(struct mpdclient *c, int event, gpointer data)
-{
- do_list_callbacks(c, c->browse_callbacks, event, data);
-}
-
-
-void
-mpdclient_install_browse_callback(struct mpdclient *c,mpdc_list_cb_t cb)
-{
- c->browse_callbacks = g_list_append(c->browse_callbacks, cb);
-}
-
-void
-mpdclient_remove_browse_callback(struct mpdclient *c, mpdc_list_cb_t cb)
-{
- c->browse_callbacks = g_list_remove(c->browse_callbacks, cb);
-}
-
-void
-mpdclient_install_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
-{
- c->error_callbacks = g_list_append(c->error_callbacks, cb);
-}
-
-void
-mpdclient_remove_error_callback(struct mpdclient *c, mpdc_error_cb_t cb)
-{
- c->error_callbacks = g_list_remove(c->error_callbacks, cb);
+ return retval;
}
mpd_entity_free(entity);
}
- c->playlist.id = mpd_status_get_queue_version(c->status);
+ c->playlist.version = mpd_status_get_queue_version(c->status);
c->song = NULL;
- /* call playlist updated callbacks */
- mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
-
return mpdclient_finish_command(c) == 0;
}
if (MPD_ERROR(c))
return false;
- mpd_send_queue_changes_meta(c->connection, c->playlist.id);
+ mpd_send_queue_changes_meta(c->connection, c->playlist.version);
while ((song = mpd_recv_song(c->connection)) != NULL) {
int pos = mpd_song_get_pos(song);
}
c->song = NULL;
- c->playlist.id = mpd_status_get_queue_version(c->status);
-
- mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL);
+ c->playlist.version = mpd_status_get_queue_version(c->status);
return mpdclient_finish_command(c) == 0;
}