X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmpdclient.c;h=4a12515752b9037366d99f1662022ea4269289e3;hb=850ad87b588dd6cbb96e5558ee671f2c95cc839d;hp=bb6c90fa9e4fc5de84fef192c19bbe5a78f48030;hpb=e1253697423aa4c1d55583c8975e02a9e2161434;p=ncmpc.git diff --git a/src/mpdclient.c b/src/mpdclient.c index bb6c90f..4a12515 100644 --- a/src/mpdclient.c +++ b/src/mpdclient.c @@ -1,215 +1,263 @@ -/* - * $Id$ - * - * (c) 2004 by Kalle Wallin - * +/* ncmpc (Ncurses MPD Client) + * (c) 2004-2009 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#include + * 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 "screen_utils.h" +#include "config.h" +#include "options.h" +#include "strfsong.h" +#include "utils.h" + #include #include #include #include -#include - -#include "config.h" -#include "ncmpc.h" -#include "support.h" -#include "mpdclient.h" -#include "options.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 ENABLE_SONG_ID -#define ENABLE_PLCHANGES +#define ENABLE_PLCHANGES -#define MPD_ERROR(c) (c==NULL || c->connection==NULL || c->connection->error) +#define BUFSIZE 1024 +static bool +MPD_ERROR(const struct mpdclient *client) +{ + return client == NULL || client->connection==NULL || + client->connection->error != MPD_ERROR_SUCCESS; +} -/* Error callbacks */ +/* filelist sorting functions */ static gint -error_cb(mpdclient_t *c, gint error, gchar *msg) -{ - GList *list = c->error_callbacks; - - if( list==NULL ) - fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg); +compare_filelistentry(gconstpointer filelist_entry1, + gconstpointer filelist_entry2) +{ + const mpd_InfoEntity *e1, *e2; + int n = 0; + + e1 = ((const filelist_entry_t *)filelist_entry1)->entity; + e2 = ((const filelist_entry_t *)filelist_entry2)->entity; + + if (e1 && e2 && e1->type == e2->type) { + switch (e1->type) { + case MPD_INFO_ENTITY_TYPE_DIRECTORY: + n = g_utf8_collate(e1->info.directory->path, + e2->info.directory->path); + break; + case MPD_INFO_ENTITY_TYPE_SONG: + break; + case MPD_INFO_ENTITY_TYPE_PLAYLISTFILE: + n = g_utf8_collate(e1->info.playlistFile->path, + e2->info.playlistFile->path); + } + } + return n; +} + +/* sort by list-format */ +gint +compare_filelistentry_format(gconstpointer filelist_entry1, + gconstpointer filelist_entry2) +{ + const mpd_InfoEntity *e1, *e2; + char key1[BUFSIZE], key2[BUFSIZE]; + int n = 0; + + e1 = ((const filelist_entry_t *)filelist_entry1)->entity; + e2 = ((const filelist_entry_t *)filelist_entry2)->entity; + + if (e1 && e2 && + e1->type == MPD_INFO_ENTITY_TYPE_SONG && + e2->type == MPD_INFO_ENTITY_TYPE_SONG) { + strfsong(key1, BUFSIZE, options.list_format, e1->info.song); + strfsong(key2, BUFSIZE, options.list_format, e2->info.song); + n = strcmp(key1,key2); + } - while(list) - { - mpdc_error_cb_t cb = list->data; - if( cb ) - cb(c, error, msg); - list=list->next; - } - mpd_clearError(c->connection); - return error; + return n; } -#ifdef DEBUG -#include "strfsong.h" -static gchar * -get_song_name(mpd_Song *song) +/* Error callbacks */ +static gint +error_cb(mpdclient_t *c, gint error, const gchar *msg) { - static gchar name[256]; + GList *list = c->error_callbacks; + + if (list == NULL) + fprintf(stderr, "error [%d]: %s\n", (error & 0xFF), msg); + + while (list) { + mpdc_error_cb_t cb = list->data; + if (cb) + cb(c, error, msg); + list = list->next; + } - strfsong(name, 256, "[%artist% - ]%title%|%file%", song); - return name; + mpd_clearError(c->connection); + return error; } -#endif /****************************************************************************/ /*** mpdclient functions ****************************************************/ /****************************************************************************/ gint -mpdclient_finish_command(mpdclient_t *c) +mpdclient_finish_command(mpdclient_t *c) { - mpd_finishCommand(c->connection); + mpd_finishCommand(c->connection); + + if (c->connection->error) { + gint error = c->connection->error; - if( c->connection->error ) - { - gchar *msg = locale_to_utf8(c->connection->errorStr); - gint error = c->connection->error; - - if( error == MPD_ERROR_ACK ) - error = error | (c->connection->errorCode << 8); + if (error == MPD_ERROR_ACK && + c->connection->errorCode == MPD_ACK_ERROR_PERMISSION && + screen_auth(c) == 0) + return 0; - error_cb(c, error, msg); - g_free(msg); - return error; - } + if (error == MPD_ERROR_ACK) + error = error | (c->connection->errorCode << 8); - return 0; + error_cb(c, error, c->connection->errorStr); + return error; + } + + return 0; } mpdclient_t * mpdclient_new(void) { - mpdclient_t *c; + mpdclient_t *c; - c = g_malloc0(sizeof(mpdclient_t)); + c = g_malloc0(sizeof(mpdclient_t)); + playlist_init(&c->playlist); + c->volume = MPD_STATUS_NO_VOLUME; - return c; + return c; } -mpdclient_t * +void mpdclient_free(mpdclient_t *c) { - mpdclient_disconnect(c); - g_list_free(c->error_callbacks); - g_list_free(c->playlist_callbacks); - g_list_free(c->browse_callbacks); - g_free(c); + mpdclient_disconnect(c); + + mpdclient_playlist_free(&c->playlist); - return NULL; + g_list_free(c->error_callbacks); + g_list_free(c->playlist_callbacks); + g_list_free(c->browse_callbacks); + g_free(c); } gint mpdclient_disconnect(mpdclient_t *c) { - if( c->connection ) - mpd_closeConnection(c->connection); - c->connection = NULL; + if (c->connection) + mpd_closeConnection(c->connection); + c->connection = NULL; + + if (c->status) + mpd_freeStatus(c->status); + c->status = NULL; - if( c->status ) - mpd_freeStatus(c->status); - c->status = NULL; + playlist_clear(&c->playlist); - if( c->playlist.list ) - mpdclient_playlist_free(&c->playlist); + if (c->song) + c->song = NULL; - if( c->song ) - c->song = NULL; - - return 0; + return 0; } gint -mpdclient_connect(mpdclient_t *c, - gchar *host, - gint port, - gfloat timeout, +mpdclient_connect(mpdclient_t *c, + gchar *host, + gint port, + gfloat _timeout, gchar *password) { - gint retval = 0; - - /* close any open connection */ - if( c->connection ) - mpdclient_disconnect(c); + gint retval = 0; - /* connect to MPD */ - c->connection = mpd_newConnection(host, port, timeout); - if( c->connection->error ) - return error_cb(c, c->connection->error, c->connection->errorStr); + /* close any open connection */ + if( c->connection ) + mpdclient_disconnect(c); - /* send password */ - if( password ) - { - mpd_sendPasswordCommand(c->connection, password); - retval = mpdclient_finish_command(c); - } - c->need_update = TRUE; + /* connect to MPD */ + c->connection = mpd_newConnection(host, port, _timeout); + if( c->connection->error ) + return error_cb(c, c->connection->error, + c->connection->errorStr); - return retval; + /* send password */ + if( password ) { + mpd_sendPasswordCommand(c->connection, password); + retval = mpdclient_finish_command(c); + } + c->need_update = TRUE; + + return retval; } gint mpdclient_update(mpdclient_t *c) { - gint retval = 0; - - if( MPD_ERROR(c) ) - return -1; - - /* free the old status */ - if( c->status ) - mpd_freeStatus(c->status); - - /* retreive new status */ - mpd_sendStatusCommand(c->connection); - c->status = mpd_getStatus(c->connection); - if( (retval=mpdclient_finish_command(c)) ) - return retval; -#ifdef DEBUG - if( c->status->error ) - D("status> %s\n", c->status->error); -#endif + gint retval = 0; + + c->volume = MPD_STATUS_NO_VOLUME; + + if (MPD_ERROR(c)) + return -1; + + /* free the old status */ + if (c->status) + mpd_freeStatus(c->status); - /* check if the playlist needs an update */ - if( c->playlist.id != c->status->playlist ) - { - if( c->playlist.list ) - retval = mpdclient_playlist_update_changes(c); - else - retval = mpdclient_playlist_update(c); - } + /* retrieve new status */ + mpd_sendStatusCommand(c->connection); + c->status = mpd_getStatus(c->connection); + if ((retval=mpdclient_finish_command(c))) + return retval; - /* update the current song */ - if( !c->song || c->status->songid != c->song->id ) - { - c->song = playlist_get_song(c, c->status->song); - } + if (c->updatingdb && c->updatingdb != c->status->updatingDb) + mpdclient_browse_callback(c, BROWSE_DB_UPDATED, NULL); + + c->updatingdb = c->status->updatingDb; + c->volume = c->status->volume; + + /* check if the playlist needs an update */ + if (c->playlist.id != c->status->playlist) { + if (playlist_is_empty(&c->playlist)) + retval = mpdclient_playlist_update_changes(c); + else + retval = mpdclient_playlist_update(c); + } + + /* update the current song */ + if (!c->song || c->status->songid != c->song->id) { + c->song = playlist_get_song(c, c->status->song); + } - c->need_update = FALSE; + c->need_update = FALSE; - return retval; + return retval; } @@ -217,591 +265,509 @@ mpdclient_update(mpdclient_t *c) /*** MPD Commands **********************************************************/ /****************************************************************************/ -gint -mpdclient_cmd_play(mpdclient_t *c, gint index) +gint +mpdclient_cmd_play(mpdclient_t *c, gint idx) { #ifdef ENABLE_SONG_ID - mpd_Song *song = playlist_get_song(c, index); + struct mpd_song *song = playlist_get_song(c, idx); - D("Play id:%d\n", song ? song->id : -1); - if( song ) - mpd_sendPlayIdCommand(c->connection, song->id); - else - mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING); + if (song) + mpd_sendPlayIdCommand(c->connection, song->id); + else + mpd_sendPlayIdCommand(c->connection, MPD_PLAY_AT_BEGINNING); #else - mpd_sendPlayCommand(c->connection, index); + mpd_sendPlayCommand(c->connection, idx); #endif - c->need_update = TRUE; - return mpdclient_finish_command(c); + c->need_update = TRUE; + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_pause(mpdclient_t *c, gint value) { - mpd_sendPauseCommand(c->connection, value); - return mpdclient_finish_command(c); + mpd_sendPauseCommand(c->connection, value); + return mpdclient_finish_command(c); } -gint +gint +mpdclient_cmd_crop(mpdclient_t *c) +{ + gint error; + mpd_Status *status; + bool playing; + int length, current; + + mpd_sendStatusCommand(c->connection); + status = mpd_getStatus(c->connection); + error = mpdclient_finish_command(c); + if (error) + return error; + + playing = status->state == MPD_STATUS_STATE_PLAY || + status->state == MPD_STATUS_STATE_PAUSE; + length = status->playlistLength; + current = status->song; + + mpd_freeStatus(status); + + if (!playing || length < 2) + return 0; + + mpd_sendCommandListBegin( c->connection ); + + while (--length >= 0) + if (length != current) + mpd_sendDeleteCommand(c->connection, length); + + mpd_sendCommandListEnd(c->connection); + + return mpdclient_finish_command(c); +} + +gint mpdclient_cmd_stop(mpdclient_t *c) { - mpd_sendStopCommand(c->connection); - return mpdclient_finish_command(c); + mpd_sendStopCommand(c->connection); + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_next(mpdclient_t *c) { - mpd_sendNextCommand(c->connection); - c->need_update = TRUE; - return mpdclient_finish_command(c); + mpd_sendNextCommand(c->connection); + c->need_update = TRUE; + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_prev(mpdclient_t *c) { - mpd_sendPrevCommand(c->connection); - c->need_update = TRUE; - return mpdclient_finish_command(c); + mpd_sendPrevCommand(c->connection); + c->need_update = TRUE; + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_seek(mpdclient_t *c, gint id, gint pos) { - D("Seek id:%d\n", id); - mpd_sendSeekIdCommand(c->connection, id, pos); - return mpdclient_finish_command(c); + mpd_sendSeekIdCommand(c->connection, id, pos); + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_shuffle(mpdclient_t *c) { - mpd_sendShuffleCommand(c->connection); - c->need_update = TRUE; - return mpdclient_finish_command(c); + mpd_sendShuffleCommand(c->connection); + c->need_update = TRUE; + return mpdclient_finish_command(c); +} + +gint +mpdclient_cmd_shuffle_range(mpdclient_t *c, guint start, guint end) +{ + mpd_sendShuffleRangeCommand(c->connection, start, end); + c->need_update = TRUE; + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_clear(mpdclient_t *c) { - gint retval = 0; + gint retval = 0; - mpd_sendClearCommand(c->connection); - retval = mpdclient_finish_command(c); - /* call playlist updated callback */ - mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL); - c->need_update = TRUE; - return retval; + mpd_sendClearCommand(c->connection); + retval = mpdclient_finish_command(c); + /* call playlist updated callback */ + mpdclient_playlist_callback(c, PLAYLIST_EVENT_CLEAR, NULL); + c->need_update = TRUE; + return retval; } -gint +gint mpdclient_cmd_repeat(mpdclient_t *c, gint value) { - mpd_sendRepeatCommand(c->connection, value); - return mpdclient_finish_command(c); + mpd_sendRepeatCommand(c->connection, value); + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_random(mpdclient_t *c, gint value) { - mpd_sendRandomCommand(c->connection, value); - return mpdclient_finish_command(c); + mpd_sendRandomCommand(c->connection, value); + return mpdclient_finish_command(c); +} + +gint +mpdclient_cmd_single(mpdclient_t *c, gint value) +{ + mpd_sendSingleCommand(c->connection, value); + return mpdclient_finish_command(c); +} + +gint +mpdclient_cmd_consume(mpdclient_t *c, gint value) +{ + mpd_sendConsumeCommand(c->connection, value); + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_crossfade(mpdclient_t *c, gint value) { - mpd_sendCrossfadeCommand(c->connection, value); - return mpdclient_finish_command(c); + mpd_sendCrossfadeCommand(c->connection, value); + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_db_update(mpdclient_t *c, gchar *path) { - mpd_sendUpdateCommand(c->connection, path ? path : ""); - return mpdclient_finish_command(c); + gint ret; + + mpd_sendUpdateCommand(c->connection, path ? path : ""); + ret = mpdclient_finish_command(c); + + if (ret == 0) + /* set updatingDb to make sure the browse callback + gets called even if the update has finished before + status is updated */ + c->updatingdb = 1; + + return ret; } -gint +gint mpdclient_cmd_volume(mpdclient_t *c, gint value) { - mpd_sendSetvolCommand(c->connection, value); - return mpdclient_finish_command(c); + mpd_sendSetvolCommand(c->connection, value); + return mpdclient_finish_command(c); } -gint -mpdclient_cmd_add(mpdclient_t *c, mpd_Song *song) -{ - gint retval = 0; +gint mpdclient_cmd_volume_up(struct mpdclient *c) +{ + if (c->status == NULL || c->status->volume == MPD_STATUS_NO_VOLUME) + return 0; - if( !song || !song->file ) - return -1; + if (c->volume == MPD_STATUS_NO_VOLUME) + c->volume = c->status->volume; + + if (c->volume >= 100) + return 0; + + return mpdclient_cmd_volume(c, ++c->volume); +} + +gint mpdclient_cmd_volume_down(struct mpdclient *c) +{ + if (c->status == NULL || c->status->volume == MPD_STATUS_NO_VOLUME) + return 0; - /* send the add command to mpd */ - mpd_sendAddCommand(c->connection, song->file); - if( (retval=mpdclient_finish_command(c)) ) - return retval; + if (c->volume == MPD_STATUS_NO_VOLUME) + c->volume = c->status->volume; + + if (c->volume <= 0) + return 0; + + return mpdclient_cmd_volume(c, --c->volume); +} + +gint +mpdclient_cmd_add_path(mpdclient_t *c, gchar *path_utf8) +{ + mpd_sendAddCommand(c->connection, path_utf8); + return mpdclient_finish_command(c); +} + +gint +mpdclient_cmd_add(mpdclient_t *c, struct mpd_song *song) +{ + gint retval = 0; + + if( !song || !song->file ) + return -1; + + /* send the add command to mpd */ + mpd_sendAddCommand(c->connection, song->file); + if( (retval=mpdclient_finish_command(c)) ) + return retval; #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_ADD - /* add the song to playlist */ - c->playlist.list = g_list_append(c->playlist.list, mpd_songDup(song)); - c->playlist.length++; + /* add the song to playlist */ + playlist_append(&c->playlist, song); - /* increment the playlist id, so we dont retrives a new playlist */ - c->playlist.id++; + /* increment the playlist id, so we don't retrieve a new playlist */ + c->playlist.id++; - /* call playlist updated callback */ - mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song); + /* call playlist updated callback */ + mpdclient_playlist_callback(c, PLAYLIST_EVENT_ADD, (gpointer) song); #else - c->need_update = TRUE; + c->need_update = TRUE; #endif - return 0; + return 0; } gint -mpdclient_cmd_delete(mpdclient_t *c, gint index) +mpdclient_cmd_delete(mpdclient_t *c, gint idx) { - gint retval = 0; - mpd_Song *song = playlist_get_song(c, index); + gint retval = 0; + struct mpd_song *song; - if( !song ) - return -1; + if (idx < 0 || (guint)idx >= playlist_length(&c->playlist)) + return -1; - /* send the delete command to mpd */ + song = playlist_get(&c->playlist, idx); + + /* send the delete command to mpd */ #ifdef ENABLE_SONG_ID - D("Delete id:%d\n", song->id); - mpd_sendDeleteIdCommand(c->connection, song->id); + mpd_sendDeleteIdCommand(c->connection, song->id); #else - mpd_sendDeleteCommand(c->connection, index); + mpd_sendDeleteCommand(c->connection, idx); #endif - if( (retval=mpdclient_finish_command(c)) ) - return retval; + if( (retval=mpdclient_finish_command(c)) ) + return retval; #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_DELETE - /* increment the playlist id, so we dont retrive a new playlist */ - c->playlist.id++; + /* increment the playlist id, so we don't retrieve a new playlist */ + c->playlist.id++; - /* remove the song from the playlist */ - c->playlist.list = g_list_remove(c->playlist.list, (gpointer) song); - c->playlist.length = g_list_length(c->playlist.list); + /* remove the song from the playlist */ + playlist_remove_reuse(&c->playlist, idx); - /* call playlist updated callback */ - mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song); + /* call playlist updated callback */ + mpdclient_playlist_callback(c, PLAYLIST_EVENT_DELETE, (gpointer) song); - /* remove references to the song */ - if( c->song == song ) - { - c->song = NULL; - c->need_update = TRUE; - } + /* remove references to the song */ + if (c->song == song) { + c->song = NULL; + c->need_update = TRUE; + } - /* free song */ - mpd_freeSong(song); + mpd_freeSong(song); #else - c->need_update = TRUE; + c->need_update = TRUE; #endif - return 0; + return 0; } gint mpdclient_cmd_move(mpdclient_t *c, gint old_index, gint new_index) { - gint n, index1, index2; - GList *item1, *item2; - gpointer data1, data2; - mpd_Song *song1, *song2; + gint n; + struct mpd_song *song1, *song2; - if( old_index==new_index || new_index<0 || new_index>=c->playlist.length ) - return -1; + if (old_index == new_index || new_index < 0 || + (guint)new_index >= c->playlist.list->len) + return -1; - song1 = playlist_get_song(c, old_index); - song2 = playlist_get_song(c, new_index); + song1 = playlist_get(&c->playlist, old_index); + song2 = playlist_get(&c->playlist, new_index); - /* send the move command to mpd */ + /* send the move command to mpd */ #ifdef ENABLE_SONG_ID - D("Swaping id:%d with id:%d\n", song1->id, song2->id); - mpd_sendSwapIdCommand(c->connection, song1->id, song2->id); + mpd_sendSwapIdCommand(c->connection, song1->id, song2->id); #else - D("Moving index %d to id:%d\n", old_index, new_index); - mpd_sendMoveCommand(c->connection, old_index, new_index); + mpd_sendMoveCommand(c->connection, old_index, new_index); #endif - if( (n=mpdclient_finish_command(c)) ) - return n; + if( (n=mpdclient_finish_command(c)) ) + return n; #ifdef ENABLE_FANCY_PLAYLIST_MANAGMENT_CMD_MOVE - /* update the songs position field */ - n = song1->pos; - song1->pos = song2->pos; - song2->pos = n; - index1 = MIN(old_index, new_index); - index2 = MAX(old_index, new_index); - /* retreive the list items */ - item1 = g_list_nth(c->playlist.list, index1); - item2 = g_list_nth(c->playlist.list, index2); - /* retrieve the songs */ - data1 = item1->data; - data2 = item2->data; - - /* move the second item */ - c->playlist.list = g_list_remove(c->playlist.list, data2); - c->playlist.list = g_list_insert_before(c->playlist.list, item1, data2); - - /* move the first item */ - if( index2-index1 >1 ) - { - item2 = g_list_nth(c->playlist.list, index2); - c->playlist.list = g_list_remove(c->playlist.list, data1); - c->playlist.list = g_list_insert_before(c->playlist.list, item2, data1); - } - - /* increment the playlist id, so we dont retrives a new playlist */ - c->playlist.id++; + /* update the playlist */ + playlist_swap(&c->playlist, old_index, new_index); + + /* increment the playlist id, so we don't retrieve a new playlist */ + c->playlist.id++; #else - c->need_update = TRUE; -#endif + c->need_update = TRUE; +#endif - /* call playlist updated callback */ - mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index); + /* call playlist updated callback */ + mpdclient_playlist_callback(c, PLAYLIST_EVENT_MOVE, (gpointer) &new_index); - return 0; + return 0; } -gint -mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename) +gint +mpdclient_cmd_save_playlist(mpdclient_t *c, gchar *filename_utf8) { - gint retval = 0; - gchar *filename_utf8 = locale_to_utf8(filename); + gint retval = 0; - mpd_sendSaveCommand(c->connection, filename_utf8); - if( (retval=mpdclient_finish_command(c)) == 0 ) - mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL); - g_free(filename_utf8); - return retval; + mpd_sendSaveCommand(c->connection, filename_utf8); + if ((retval = mpdclient_finish_command(c)) == 0) + mpdclient_browse_callback(c, BROWSE_PLAYLIST_SAVED, NULL); + return retval; } -gint +gint mpdclient_cmd_load_playlist(mpdclient_t *c, gchar *filename_utf8) { - mpd_sendLoadCommand(c->connection, filename_utf8); - c->need_update = TRUE; - return mpdclient_finish_command(c); + mpd_sendLoadCommand(c->connection, filename_utf8); + c->need_update = TRUE; + return mpdclient_finish_command(c); } -gint +gint mpdclient_cmd_delete_playlist(mpdclient_t *c, gchar *filename_utf8) { - gint retval = 0; + gint retval = 0; - mpd_sendRmCommand(c->connection, filename_utf8); - if( (retval=mpdclient_finish_command(c)) == 0 ) - mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL); - return retval; + mpd_sendRmCommand(c->connection, filename_utf8); + if ((retval = mpdclient_finish_command(c)) == 0) + mpdclient_browse_callback(c, BROWSE_PLAYLIST_DELETED, NULL); + return retval; } /****************************************************************************/ -/*** Callback managment functions *******************************************/ +/*** Callback management functions ******************************************/ /****************************************************************************/ + static void do_list_callbacks(mpdclient_t *c, GList *list, gint event, gpointer data) { - while(list) - { - mpdc_list_cb_t fn = list->data; + while (list) { + mpdc_list_cb_t fn = list->data; - fn(c, event, data); - list=list->next; - } + fn(c, event, data); + list = list->next; + } } void mpdclient_playlist_callback(mpdclient_t *c, int event, gpointer data) { - do_list_callbacks(c, c->playlist_callbacks, event, data); + do_list_callbacks(c, c->playlist_callbacks, event, data); } void mpdclient_install_playlist_callback(mpdclient_t *c,mpdc_list_cb_t cb) { - c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb); + c->playlist_callbacks = g_list_append(c->playlist_callbacks, cb); } void mpdclient_remove_playlist_callback(mpdclient_t *c, mpdc_list_cb_t cb) { - c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb); + c->playlist_callbacks = g_list_remove(c->playlist_callbacks, cb); } void mpdclient_browse_callback(mpdclient_t *c, int event, gpointer data) { - do_list_callbacks(c, c->browse_callbacks, event, data); + do_list_callbacks(c, c->browse_callbacks, event, data); } void mpdclient_install_browse_callback(mpdclient_t *c,mpdc_list_cb_t cb) { - c->browse_callbacks = g_list_append(c->browse_callbacks, cb); + c->browse_callbacks = g_list_append(c->browse_callbacks, cb); } void mpdclient_remove_browse_callback(mpdclient_t *c, mpdc_list_cb_t cb) { - c->browse_callbacks = g_list_remove(c->browse_callbacks, cb); + c->browse_callbacks = g_list_remove(c->browse_callbacks, cb); } void mpdclient_install_error_callback(mpdclient_t *c, mpdc_error_cb_t cb) { - c->error_callbacks = g_list_append(c->error_callbacks, cb); + c->error_callbacks = g_list_append(c->error_callbacks, cb); } void mpdclient_remove_error_callback(mpdclient_t *c, mpdc_error_cb_t cb) { - c->error_callbacks = g_list_remove(c->error_callbacks, cb); + c->error_callbacks = g_list_remove(c->error_callbacks, cb); } + /****************************************************************************/ -/*** Playlist managment functions *******************************************/ +/*** Playlist management functions ******************************************/ /****************************************************************************/ -gint -mpdclient_playlist_free(mpdclient_playlist_t *playlist) -{ - GList *list = g_list_first(playlist->list); - - while(list) - { - mpd_Song *song = (mpd_Song *) list->data; - mpd_freeSong(song); - list=list->next; - } - g_list_free(playlist->list); - memset(playlist, 0, sizeof(mpdclient_playlist_t)); - return 0; -} - /* update playlist */ -gint +gint mpdclient_playlist_update(mpdclient_t *c) { - mpd_InfoEntity *entity; - - D("mpdclient_playlist_update() [%lld]\n", c->status->playlist); + mpd_InfoEntity *entity; - if( MPD_ERROR(c) ) - return -1; + if (MPD_ERROR(c)) + return -1; - if( c->playlist.list ) - mpdclient_playlist_free(&c->playlist); + playlist_clear(&c->playlist); - mpd_sendPlaylistInfoCommand(c->connection,-1); - while( (entity=mpd_getNextInfoEntity(c->connection)) ) - { - if(entity->type==MPD_INFO_ENTITY_TYPE_SONG) - { - mpd_Song *song = mpd_songDup(entity->info.song); + mpd_sendPlaylistInfoCommand(c->connection,-1); + while ((entity = mpd_getNextInfoEntity(c->connection))) { + if (entity->type == MPD_INFO_ENTITY_TYPE_SONG) + playlist_append(&c->playlist, entity->info.song); - c->playlist.list = g_list_append(c->playlist.list, (gpointer) song); - c->playlist.length++; + mpd_freeInfoEntity(entity); } - mpd_freeInfoEntity(entity); - } - c->playlist.id = c->status->playlist; - c->song = NULL; - c->playlist.updated = TRUE; - /* call playlist updated callbacks */ - mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL); + c->playlist.id = c->status->playlist; + c->song = NULL; - return mpdclient_finish_command(c); -} - -#ifdef ENABLE_PLCHANGES + /* call playlist updated callbacks */ + mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL); -gint -mpdclient_compare_songs(gconstpointer a, gconstpointer b) -{ - mpd_Song *song1 = (mpd_Song *) a; - mpd_Song *song2 = (mpd_Song *) b; - - return song1->pos - song2->pos; + return mpdclient_finish_command(c); } - +#ifdef ENABLE_PLCHANGES /* update playlist (plchanges) */ - -gint +gint mpdclient_playlist_update_changes(mpdclient_t *c) { - mpd_InfoEntity *entity; - - D("mpdclient_playlist_update_changes() [%lld -> %lld]\n", - c->status->playlist, c->playlist.id); - - if( MPD_ERROR(c) ) - return -1; - - mpd_sendPlChangesCommand(c->connection, c->playlist.id); + mpd_InfoEntity *entity; - while( (entity=mpd_getNextInfoEntity(c->connection)) != NULL ) - { + if (MPD_ERROR(c)) + return -1; - if(entity->type==MPD_INFO_ENTITY_TYPE_SONG) - { - mpd_Song *song = entity->info.song; - GList *item; + mpd_sendPlChangesCommand(c->connection, c->playlist.id); - item = playlist_lookup(c, song->id); + while ((entity = mpd_getNextInfoEntity(c->connection)) != NULL) { + struct mpd_song *song = entity->info.song; -#ifdef DEBUG - if( item ) - D("Changing index:%d, pos:%d, id:%d => %s\n", - g_list_position(c->playlist.list, item), - song->pos, song->id, get_song_name(song)); - else - D("Unable to find pos:%d, id:%d => %s\n", - song->pos, song->id, get_song_name(song)); -#endif - - if( item && item->data) - { - GList *old; - gint index = g_list_position(c->playlist.list, item); - - /* remove previous song at song->pos */ - while( song->pos != (index=g_list_position(c->playlist.list, item)) - && - (old=g_list_nth(c->playlist.list, song->pos)) ) - { - D("Removing item with index %d id:%d (%d)\n", - song->pos, old ? ((mpd_Song *) old->data)->id : -1, index); - if( item->data == c->song ) - c->song = NULL; - mpd_freeSong((mpd_Song *) old->data); - old->data = NULL; - c->playlist.list = g_list_delete_link(c->playlist.list, old); - c->playlist.length = g_list_length(c->playlist.list); + if (song->pos >= 0 && (guint)song->pos < c->playlist.list->len) { + /* update song */ + playlist_replace(&c->playlist, song->pos, song); + } else { + /* add a new song */ + playlist_append(&c->playlist, song); } - /* Update playlist entry */ - mpd_freeSong((mpd_Song *) item->data); - item->data = mpd_songDup(song); - if( c->song && c->song->id == song->id ) - c->song = item->data; - - } - else - { - /* Add a new playlist entry */ - D("Adding pos:%d, id;%d - %s\n", - song->pos, song->id, get_song_name(song)); - c->playlist.list = g_list_append(c->playlist.list, - (gpointer) mpd_songDup(song)); - c->playlist.length++; - } + mpd_freeInfoEntity(entity); } - mpd_freeInfoEntity(entity); - } - mpd_finishCommand(c->connection); - - while( g_list_length(c->playlist.list) > c->status->playlistLength ) - { - GList *item = g_list_last(c->playlist.list); - - /* Remove the last playlist entry */ - mpd_freeSong((mpd_Song *) item->data); - c->playlist.list = g_list_delete_link(c->playlist.list, item); - c->playlist.length--; - D("Removed the last playlist entry\n"); - } - - c->playlist.id = c->status->playlist; - c->playlist.updated = TRUE; - - mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL); - return 0; -} -#else -gint -mpdclient_playlist_update_changes(mpdclient_t *c) -{ - return mpdclient_playlist_update(c); -} -#endif - -mpd_Song * -playlist_get_song(mpdclient_t *c, gint index) -{ - return (mpd_Song *) g_list_nth_data(c->playlist.list, index); -} - -GList * -playlist_lookup(mpdclient_t *c, int id) -{ - GList *list = g_list_first(c->playlist.list); + /* remove trailing songs */ + while ((guint)c->status->playlistLength < c->playlist.list->len) { + guint pos = c->playlist.list->len - 1; - while( list ) - { - mpd_Song *song = (mpd_Song *) list->data; - if( song->id == id ) - return list; - list=list->next; - } - return NULL; -} + /* Remove the last playlist entry */ + playlist_remove(&c->playlist, pos); + } -mpd_Song * -playlist_lookup_song(mpdclient_t *c, gint id) -{ - GList *list = c->playlist.list; + c->song = NULL; + c->playlist.id = c->status->playlist; - while( list ) - { - mpd_Song *song = (mpd_Song *) list->data; - if( song->id == id ) - return song; - list=list->next; - } - return NULL; -} + mpdclient_playlist_callback(c, PLAYLIST_EVENT_UPDATED, NULL); -gint -playlist_get_index(mpdclient_t *c, mpd_Song *song) -{ - return g_list_index(c->playlist.list, song); -} - -gint -playlist_get_index_from_id(mpdclient_t *c, gint id) -{ - return g_list_index(c->playlist.list, playlist_lookup_song(c, id)); + return 0; } +#else gint -playlist_get_index_from_file(mpdclient_t *c, gchar *filename) +mpdclient_playlist_update_changes(mpdclient_t *c) { - GList *list = c->playlist.list; - gint i=0; - - while( list ) - { - mpd_Song *song = (mpd_Song *) list->data; - if( strcmp(song->file, filename ) == 0 ) - return i; - list=list->next; - i++; - } - return -1; + return mpdclient_playlist_update(c); } +#endif /****************************************************************************/ @@ -809,117 +775,122 @@ playlist_get_index_from_file(mpdclient_t *c, gchar *filename) /****************************************************************************/ mpdclient_filelist_t * -mpdclient_filelist_free(mpdclient_filelist_t *filelist) +mpdclient_filelist_get(mpdclient_t *c, const gchar *path) { - GList *list = g_list_first(filelist->list); + mpdclient_filelist_t *filelist; + mpd_InfoEntity *entity; + + mpd_sendLsInfoCommand(c->connection, path); + filelist = filelist_new(path); + if (path && path[0] && strcmp(path, "/")) + /* add a dummy entry for ./.. */ + filelist_append(filelist, NULL); - D("mpdclient_filelist_free()\n"); - while( list!=NULL ) - { - filelist_entry_t *entry = list->data; + while ((entity=mpd_getNextInfoEntity(c->connection))) { + filelist_append(filelist, entity); + } - if( entry->entity ) - mpd_freeInfoEntity(entry->entity); - g_free(entry); - list=list->next; - } - g_list_free(filelist->list); - g_free(filelist->path); - filelist->path = NULL; - filelist->list = NULL; - filelist->length = 0; - g_free(filelist); + /* If there's an error, ignore it. We'll return an empty filelist. */ + mpdclient_finish_command(c); - return NULL; -} + filelist_sort_dir_play(filelist, compare_filelistentry); + return filelist; +} mpdclient_filelist_t * -mpdclient_filelist_get(mpdclient_t *c, gchar *path) -{ - mpdclient_filelist_t *filelist; - mpd_InfoEntity *entity; - gchar *path_utf8 = locale_to_utf8(path); - - D("mpdclient_filelist_get(%s)\n", path); - mpd_sendLsInfoCommand(c->connection, path_utf8); - filelist = g_malloc0(sizeof(mpdclient_filelist_t)); - if( path && path[0] && strcmp(path, "/") ) - { - /* add a dummy entry for ./.. */ - filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t)); - entry->entity = NULL; - filelist->list = g_list_append(filelist->list, (gpointer) entry); - filelist->length++; - } - - while( (entity=mpd_getNextInfoEntity(c->connection)) ) - { - filelist_entry_t *entry = g_malloc0(sizeof(filelist_entry_t)); - - entry->entity = entity; - filelist->list = g_list_append(filelist->list, (gpointer) entry); - filelist->length++; - } - - if( mpdclient_finish_command(c) ) - { - g_free(path_utf8); - return mpdclient_filelist_free(filelist); - } - - g_free(path_utf8); - filelist->path = g_strdup(path); - filelist->updated = TRUE; - - return filelist; +mpdclient_filelist_search(mpdclient_t *c, + int exact_match, + int table, + gchar *filter_utf8) +{ + mpdclient_filelist_t *filelist; + mpd_InfoEntity *entity; + + if (exact_match) + mpd_sendFindCommand(c->connection, table, filter_utf8); + else + mpd_sendSearchCommand(c->connection, table, filter_utf8); + filelist = filelist_new(NULL); + + while ((entity=mpd_getNextInfoEntity(c->connection))) + filelist_append(filelist, entity); + + if (mpdclient_finish_command(c)) { + filelist_free(filelist); + return NULL; + } + + return filelist; } mpdclient_filelist_t * mpdclient_filelist_update(mpdclient_t *c, mpdclient_filelist_t *filelist) { - if( filelist != NULL ) - { - gchar *path = g_strdup(filelist->path); + if (filelist != NULL) { + gchar *path = g_strdup(filelist->path); - filelist = mpdclient_filelist_free(filelist); - filelist = mpdclient_filelist_get(c, path); - g_free(path); - return filelist; - } - return NULL; + filelist_free(filelist); + filelist = mpdclient_filelist_get(c, path); + g_free(path); + return filelist; + } + return NULL; } -filelist_entry_t * -mpdclient_filelist_find_song(mpdclient_filelist_t *fl, mpd_Song *song) +int +mpdclient_filelist_add_all(mpdclient_t *c, mpdclient_filelist_t *fl) { - GList *list = g_list_first(fl->list); + guint i; - while( list && song) - { - filelist_entry_t *entry = list->data; - mpd_InfoEntity *entity = entry->entity; + if (filelist_is_empty(fl)) + return 0; - if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) - { - mpd_Song *song2 = entity->info.song; + mpd_sendCommandListBegin(c->connection); - if( strcmp(song->file, song2->file) == 0 ) - { - return entry; - } - } - list = list->next; - } - return NULL; -} + for (i = 0; i < filelist_length(fl); ++i) { + filelist_entry_t *entry = filelist_get(fl, i); + mpd_InfoEntity *entity = entry->entity; + if (entity && entity->type == MPD_INFO_ENTITY_TYPE_SONG) { + struct mpd_song *song = entity->info.song; + mpd_sendAddCommand(c->connection, song->file); + } + } + mpd_sendCommandListEnd(c->connection); + return mpdclient_finish_command(c); +} +GList * +mpdclient_get_artists(mpdclient_t *c) +{ + gchar *str = NULL; + GList *list = NULL; + mpd_sendListCommand(c->connection, MPD_TABLE_ARTIST, NULL); + while ((str = mpd_getNextArtist(c->connection))) + list = g_list_append(list, (gpointer) str); + if (mpdclient_finish_command(c)) + return string_list_free(list); + return list; +} +GList * +mpdclient_get_albums(mpdclient_t *c, gchar *artist_utf8) +{ + gchar *str = NULL; + GList *list = NULL; + + mpd_sendListCommand(c->connection, MPD_TABLE_ALBUM, artist_utf8); + while ((str = mpd_getNextAlbum(c->connection))) + list = g_list_append(list, (gpointer) str); + if (mpdclient_finish_command(c)) + return string_list_free(list); + return list; +}