# HG changeset patch # User Fabien Ninoles # Date 1211174988 14400 # Node ID a17355c8ffbd66f18d25ca9a19ec5c5522d83929 First import of mpd-client. diff -r 000000000000 -r a17355c8ffbd .hgignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.hgignore Mon May 19 01:29:48 2008 -0400 @@ -0,0 +1,5 @@ +^\.hg +^\.mq +syntax: glob +status +guards diff -r 000000000000 -r a17355c8ffbd mpd-client --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mpd-client Mon May 19 01:29:48 2008 -0400 @@ -0,0 +1,3633 @@ +diff -r fb5042644071 debian/patches/00list +--- a/debian/patches/00list Mon May 19 00:17:05 2008 -0400 ++++ b/debian/patches/00list Mon May 19 01:12:49 2008 -0400 +@@ -1,2 +1,4 @@ + 01verbose-compilation + 02compile-with-gcc-4.3 ++03mpd-client ++04mpd-client-config-init +diff -r fb5042644071 debian/patches/03mpd-client.dpatch +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/debian/patches/03mpd-client.dpatch Mon May 19 01:12:49 2008 -0400 +@@ -0,0 +1,3597 @@ ++#! /bin/sh /usr/share/dpatch/dpatch-run ++## mpd-client.dpatch by Fabien Niñoles ++## ++## All lines beginning with `## DP:' are a description of the patch. ++## DP: Add mpd to imms clients. ++ ++@DPATCH@ ++ ++diff -r 171db9560cb5 clients/mpd/immsmpd.cc ++--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++++ b/clients/mpd/immsmpd.cc Mon May 19 00:21:51 2008 -0400 ++@@ -0,0 +1,282 @@ +++#include "immsutil.h" +++#include "clientstub.h" +++ +++#include +++#include +++ +++#include "mpdinterface.h" +++#include +++ +++ +++using std::endl; +++using std::list; +++using namespace mpd_interface; +++ +++// interface for communication with IMMS server +++// comes from clientstub.h +++struct MPDOps; +++typedef IMMSClient MPDClient; +++ +++int poll_time = 250; // miliseconds +++const double reconnect_interval = 1.0; // seconds +++GMainLoop *loop = nullptr; +++GSource* ts; +++ +++MPDClient imms; +++Player mpd; +++list playqueue; +++const list::size_type pq_capacity = 1; +++ +++ +++// that's the main function - it's called regularly and handles all the +++// IMMS and MPD communication and decides what to do when anything happens +++gboolean do_events(void *unused); +++ +++// determines whether the currently playing song has been selected +++// specifically by the user +++bool next_jumped(); +++ +++void notify_song_ended(const Song& song, double elapsed_time, bool jumped); +++ +++// resets the elapsed_timer and sets the jumped flag, too +++void notify_song_started(const Song& song, double& elapsed_time, +++ bool& jumped); +++ +++void update_playqueue(); +++bool playqueue_contains(int song); +++ +++void quit(int signum); +++ +++ +++struct MPDOps +++{ +++ // IMMS server's suggestion of the next song +++ static void set_next(int next) +++ { +++ if(playqueue.size() < pq_capacity) { +++ if(mpd.song(current).path() == mpd.song(next).path() || +++ mpd.song(previous).path() == mpd.song(next).path() || +++ playqueue_contains(next) || next >= mpd.playlist_length()) { +++ update_playqueue(); +++ } +++ else { +++ playqueue.push_back(next); +++ } +++ } +++ } +++ // no idea, works just peach as it is +++ // asked mag about it, waiting for an answer +++ // TODO if he doesn't answer, dig it out +++ static void reset_selection() { } +++ // path of the song at the given pos for IMMS server +++ static string get_item(int index) {return mpd.song(index).path(); } +++ // size of the playlist for the IMMS server +++ static int get_length() { return mpd.playlist_length(); } +++}; +++ +++int main(int argc, char **argv) +++{ +++ // glib initialization +++ loop = g_main_loop_new(nullptr, FALSE); +++ +++ signal(SIGINT, quit); +++ signal(SIGTERM, quit); +++ signal(SIGPIPE, SIG_IGN); +++ +++ ts = g_timeout_source_new(poll_time); +++ g_source_attach(ts, nullptr); +++ g_source_set_callback(ts, (GSourceFunc)do_events, nullptr, nullptr); +++ // end of glib init +++ +++ g_main_loop_run(loop); +++ +++ LOG(INFO) << "Exitting." << endl; +++ return 0; +++} +++ +++gboolean do_events(void *unused) +++{ +++ static const double time_inc = double(poll_time)/1000.0; +++ // the time that ACTUALLY elapsed when playing the song (in seconds) +++ static double elapsed_time = 0; +++ static bool jumped = false; +++ static double reconnect_timeout = 0; +++ +++ try { +++ if(!mpd.connected()) { +++ if(reconnect_timeout < time_inc) { +++ LOG(INFO) << "Not connected to MPD. Attempting to connect..." +++ << endl; +++ reconnect_timeout = reconnect_interval; +++ mpd.connect(); +++ LOG(INFO) << "Connected to MPD." << endl; +++ reconnect_timeout = 0; +++ } +++ else { +++ reconnect_timeout -= time_inc; +++ return TRUE; +++ } +++ } +++ if(imms.check_connection()) imms.setup(0); +++ if(!imms.isok()) return TRUE; +++ +++ mpd.refresh(); +++ +++ if(mpd.playlist_changed()) { +++ imms.playlist_changed(mpd.playlist_length()); +++ playqueue.clear(); +++ } +++ if(mpd.radnom()) update_playqueue(); +++ +++ if(mpd.status_changed()) { +++ if(mpd.status(current) == stopped) { +++ notify_song_ended(mpd.song(current), elapsed_time, jumped); +++ } +++ else if(mpd.status(previous) == stopped && +++ mpd.status(current)==playing) { +++ notify_song_started(mpd.song(current), elapsed_time, jumped); +++ +++ // clear the song_changed() flag +++ if(mpd.song_changed()) mpd.refresh(); +++ } +++ } +++ if(mpd.status(current) == playing) { +++ elapsed_time += time_inc; +++ if(mpd.song_changed()) { +++ notify_song_ended(mpd.song(previous), elapsed_time, jumped); +++ +++ if(!mpd.radnom() || playqueue.empty()) { +++ notify_song_started(mpd.song(current), elapsed_time, jumped); +++ } +++ else { +++ mpd.play_song(playqueue.front()); +++ notify_song_started(mpd.song(playqueue.front()), elapsed_time, +++ jumped); +++ playqueue.erase(playqueue.begin()); +++ update_playqueue(); +++ // this could otherwise incorrectly set song_changed() flag +++ mpd.refresh(); +++ } +++ } +++ else { +++ // fix the real timer if it's inconsistent with what player +++ // says is the elapsed time of the current song +++ if(elapsed_time - double(mpd.song(current).elapsed()) >= 3.0) { +++ if(mpd.song(current).elapsed() == 0) { +++ Song tmp_ended = mpd.song(current); +++ tmp_ended.set_length(int(elapsed_time) * 2); +++ notify_song_ended(tmp_ended, elapsed_time, jumped); +++ notify_song_started(mpd.song(current), elapsed_time, jumped); +++ jumped = false; +++ } +++ elapsed_time = mpd.song(current).elapsed(); +++ } +++ } +++ } +++ } +++ catch (connection_err) { +++ if(reconnect_timeout >= reconnect_interval) { +++ LOG(INFO) << "Connecting to MPD was unsuccessful. " +++ "Next attempt in " << reconnect_interval << " second" +++ << (reconnect_interval == 1.0 ? "":"s") << "." << endl; +++ } +++ else { +++ LOG(INFO) << "Disconnected from MPD." << endl; +++ } +++ } +++ catch (mpd_err ex) { +++ LOG(ERROR) << "Error: " << ex.message() << endl; +++ g_main_quit(loop); +++ loop = nullptr; +++ exit(1); +++ } +++ return TRUE; +++} +++ +++// determines whether the song has been played whole +++bool played_whole(double elapsed_time, int length) +++{ +++ static const double abs_limit = 20.0; +++ static const double coeficient = 0.08; +++ +++ double rel_limit = double(length) * coeficient; +++ +++ double limit = (abs_limit < rel_limit) ? abs_limit : rel_limit; +++ return double(length)-elapsed_time < limit; +++} +++// determines whether the song has been skipped early +++// (thus being set as not a prefed one) +++bool bad(double elapsed_time, int length) +++{ +++ static const double abs_limit = 30.0; +++ static const double coeficient = 0.13; +++ +++ double rel_limit = double(length) * coeficient; +++ +++ double limit = (rel_limit < abs_limit) ? rel_limit : abs_limit; +++ return elapsed_time < limit; +++} +++bool next_jumped() +++{ +++ if(mpd.radnom()) return false; +++ +++ if(mpd.song(current).pos() == mpd.song(previous).pos()+1) +++ return false; +++ if(mpd.song(current).pos() == 0 && +++ mpd.song(previous).pos() == mpd.playlist_length()-1) +++ return false; +++ +++ return true; +++} +++void notify_song_ended(const Song& song, double elapsed_time, bool jumped) +++{ +++ if(song.length() == Song::invalid_time) return; +++ +++ imms.end_song(played_whole(elapsed_time, song.length()), jumped, +++ bad(elapsed_time, song.length()) ); +++} +++ +++void notify_song_started(const Song& song, double& elapsed_timer, +++ bool& jumped) +++{ +++ imms.start_song(song.pos(), song.path()); +++ elapsed_timer = 0; +++ jumped = next_jumped(); +++ mpd.song_changed(); // clear the change notification +++} +++ +++void update_playqueue() +++{ +++ static const int timeout_value = 3; +++ static int timeout = 0; +++ +++ if(timeout == 0) +++ { +++ if(playqueue.size() < pq_capacity) { +++ list::size_type i; +++ for(i = pq_capacity - playqueue.size(); i!=0; i--) { +++ imms.select_next(); +++ } +++ timeout = timeout_value; +++ } +++ } +++ else --timeout; +++} +++ +++bool playqueue_contains(int song) +++{ +++ list::const_iterator i; +++ for(i = playqueue.begin(); i!= playqueue.end(); i++) { +++ if (*i == song) return true; +++ } +++ return false; +++} +++ +++void quit(int signum) +++{ +++ if (loop) g_main_quit(loop); +++ loop = nullptr; +++ signal(signum, SIG_DFL); +++} +++ +++ ++diff -r 171db9560cb5 clients/mpd/libmpdclient.c ++--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++++ b/clients/mpd/libmpdclient.c Mon May 19 00:21:51 2008 -0400 ++@@ -0,0 +1,1955 @@ +++/* libmpdclient +++ (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com) +++ This project's homepage is: http://www.musicpd.org +++ +++ Redistribution and use in source and binary forms, with or without +++ modification, are permitted provided that the following conditions +++ are met: +++ +++ - Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ +++ - Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in the +++ documentation and/or other materials provided with the distribution. +++ +++ - Neither the name of the Music Player Daemon nor the names of its +++ contributors may be used to endorse or promote products derived from +++ this software without specific prior written permission. +++ +++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +++ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +++ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +++ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +++ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +++ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +++ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +++ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +++ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +++ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +++ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +++*/ +++ +++#include "libmpdclient.h" +++ +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++#include +++ +++#ifdef WIN32 +++# include +++# include +++#else +++# include +++# include +++# include +++# include +++#endif +++ +++/* (bits+1)/3 (plus the sign character) */ +++#define INTLEN ((sizeof(int) * CHAR_BIT + 1) / 3 + 1) +++#define LONGLONGLEN ((sizeof(long long) * CHAR_BIT + 1) / 3 + 1) +++ +++#define COMMAND_LIST 1 +++#define COMMAND_LIST_OK 2 +++ +++#ifndef MPD_NO_GAI +++# ifdef AI_ADDRCONFIG +++# define MPD_HAVE_GAI +++# endif +++#endif +++ +++#ifndef MSG_DONTWAIT +++# define MSG_DONTWAIT 0 +++#endif +++ +++#ifdef WIN32 +++# define SELECT_ERRNO_IGNORE (errno == WSAEINTR || errno == WSAEINPROGRESS) +++# define SENDRECV_ERRNO_IGNORE SELECT_ERRNO_IGNORE +++#else +++# define SELECT_ERRNO_IGNORE (errno == EINTR) +++# define SENDRECV_ERRNO_IGNORE (errno == EINTR || errno == EAGAIN) +++# define winsock_dll_error(c) 0 +++# define closesocket(s) close(s) +++# define WSACleanup() do { /* nothing */ } while (0) +++#endif +++ +++#ifdef WIN32 +++static int winsock_dll_error(mpd_Connection *connection) +++{ +++ WSADATA wsaData; +++ if ((WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0 || +++ LOBYTE(wsaData.wVersion) != 2 || +++ HIBYTE(wsaData.wVersion) != 2 ) { +++ strcpy(connection->errorStr, +++ "Could not find usable WinSock DLL."); +++ connection->error = MPD_ERROR_SYSTEM; +++ return 1; +++ } +++ return 0; +++} +++ +++static int do_connect_fail(mpd_Connection *connection, +++ const struct sockaddr *serv_addr, int addrlen) +++{ +++ int iMode = 1; /* 0 = blocking, else non-blocking */ +++ ioctlsocket(connection->sock, FIONBIO, (u_long FAR*) &iMode); +++ return (connect(connection->sock,serv_addr,addrlen) == SOCKET_ERROR +++ && WSAGetLastError() != WSAEWOULDBLOCK); +++} +++#else /* !WIN32 (sane operating systems) */ +++static int do_connect_fail(mpd_Connection *connection, +++ const struct sockaddr *serv_addr, int addrlen) +++{ +++ int flags = fcntl(connection->sock, F_GETFL, 0); +++ fcntl(connection->sock, F_SETFL, flags | O_NONBLOCK); +++ return (connect(connection->sock,serv_addr,addrlen)<0 && +++ errno!=EINPROGRESS); +++} +++#endif /* !WIN32 */ +++ +++#ifdef MPD_HAVE_GAI +++static int mpd_connect(mpd_Connection * connection, const char * host, int port, +++ float timeout) +++{ +++ int error; +++ char service[INTLEN+1]; +++ struct addrinfo hints; +++ struct addrinfo *res = NULL; +++ struct addrinfo *addrinfo = NULL; +++ +++ /** +++ * Setup hints +++ */ +++ hints.ai_flags = AI_ADDRCONFIG; +++ hints.ai_family = PF_UNSPEC; +++ hints.ai_socktype = SOCK_STREAM; +++ hints.ai_protocol = IPPROTO_TCP; +++ hints.ai_addrlen = 0; +++ hints.ai_addr = NULL; +++ hints.ai_canonname = NULL; +++ hints.ai_next = NULL; +++ +++ snprintf(service, sizeof(service), "%i", port); +++ +++ error = getaddrinfo(host, service, &hints, &addrinfo); +++ +++ if (error) { +++ snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH, +++ "host \"%s\" not found: %s", +++ host, gai_strerror(error)); +++ connection->error = MPD_ERROR_UNKHOST; +++ return -1; +++ } +++ +++ for (res = addrinfo; res; res = res->ai_next) { +++ /* create socket */ +++ connection->sock = socket(res->ai_family, SOCK_STREAM, +++ res->ai_protocol); +++ if (connection->sock < 0) { +++ snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH, +++ "problems creating socket: %s", +++ strerror(errno)); +++ connection->error = MPD_ERROR_SYSTEM; +++ freeaddrinfo(addrinfo); +++ return -1; +++ } +++ +++ mpd_setConnectionTimeout(connection, timeout); +++ +++ /* connect stuff */ +++ if (do_connect_fail(connection, +++ res->ai_addr, res->ai_addrlen)) { +++ /* try the next address family */ +++ closesocket(connection->sock); +++ connection->sock = -1; +++ continue; +++ } +++ } +++ +++ freeaddrinfo(addrinfo); +++ +++ if (connection->sock < 0) { +++ snprintf(connection->errorStr, MPD_ERRORSTR_MAX_LENGTH, +++ "problems connecting to \"%s\" on port %i: %s", +++ host, port, strerror(errno)); +++ connection->error = MPD_ERROR_CONNPORT; +++ +++ return -1; +++ } +++ +++ return 0; +++} +++#else /* !MPD_HAVE_GAI */ +++static int mpd_connect(mpd_Connection * connection, const char * host, int port, +++ float timeout) +++{ +++ struct hostent * he; +++ struct sockaddr * dest; +++ int destlen; +++ struct sockaddr_in sin; +++ +++ if(!(he=gethostbyname(host))) { +++ snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH, +++ "host \"%s\" not found",host); +++ connection->error = MPD_ERROR_UNKHOST; +++ return -1; +++ } +++ +++ memset(&sin,0,sizeof(struct sockaddr_in)); +++ /*dest.sin_family = he->h_addrtype;*/ +++ sin.sin_family = AF_INET; +++ sin.sin_port = htons(port); +++ +++ switch(he->h_addrtype) { +++ case AF_INET: +++ memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr, +++ he->h_length); +++ dest = (struct sockaddr *)&sin; +++ destlen = sizeof(struct sockaddr_in); +++ break; +++ default: +++ strcpy(connection->errorStr,"address type is not IPv4"); +++ connection->error = MPD_ERROR_SYSTEM; +++ return -1; +++ break; +++ } +++ +++ if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) { +++ strcpy(connection->errorStr,"problems creating socket"); +++ connection->error = MPD_ERROR_SYSTEM; +++ return -1; +++ } +++ +++ mpd_setConnectionTimeout(connection,timeout); +++ +++ /* connect stuff */ +++ if (do_connect_fail(connection, dest, destlen)) { +++ snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH, +++ "problems connecting to \"%s\" on port" +++ " %i",host,port); +++ connection->error = MPD_ERROR_CONNPORT; +++ return -1; +++ } +++ +++ return 0; +++} +++#endif /* !MPD_HAVE_GAI */ +++ +++char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES] = +++{ +++ "Artist", +++ "Album", +++ "Title", +++ "Track", +++ "Name", +++ "Genre", +++ "Date", +++ "Composer", +++ "Performer", +++ "Comment", +++ "Disc", +++ "Filename", +++ "Any" +++}; +++ +++static char * mpd_sanitizeArg(const char * arg) { +++ size_t i; +++ char * ret; +++ register const char *c; +++ register char *rc; +++ +++ /* instead of counting in that loop above, just +++ * use a bit more memory and half running time +++ */ +++ ret = (char *)malloc(strlen(arg) * 2 + 1); +++ +++ c = arg; +++ rc = ret; +++ for(i = strlen(arg)+1; i != 0; --i) { +++ if(*c=='"' || *c=='\\') +++ *rc++ = '\\'; +++ *(rc++) = *(c++); +++ } +++ +++ return ret; +++} +++ +++static mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value) +++{ +++ mpd_ReturnElement * ret = (mpd_ReturnElement *)malloc(sizeof(mpd_ReturnElement)); +++ +++ ret->name = strdup(name); +++ ret->value = strdup(value); +++ +++ return ret; +++} +++ +++static void mpd_freeReturnElement(mpd_ReturnElement * re) { +++ free(re->name); +++ free(re->value); +++ free(re); +++} +++ +++void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) { +++ connection->timeout.tv_sec = (int)timeout; +++ connection->timeout.tv_usec = (int)(timeout*1e6 - +++ connection->timeout.tv_sec*1000000 + +++ 0.5); +++} +++ +++static int mpd_parseWelcome(mpd_Connection * connection, const char * host, int port, +++ char * rt, char * output) { +++ char * tmp; +++ char * test; +++ int i; +++ +++ if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) { +++ snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH, +++ "mpd not running on port %i on host \"%s\"", +++ port,host); +++ connection->error = MPD_ERROR_NOTMPD; +++ return 1; +++ } +++ +++ tmp = &output[strlen(MPD_WELCOME_MESSAGE)]; +++ +++ for(i=0;i<3;i++) { +++ if(tmp) connection->version[i] = strtol(tmp,&test,10); +++ +++ if (!tmp || (test[0] != '.' && test[0] != '\0')) { +++ snprintf(connection->errorStr, +++ MPD_ERRORSTR_MAX_LENGTH, +++ "error parsing version number at " +++ "\"%s\"", +++ &output[strlen(MPD_WELCOME_MESSAGE)]); +++ connection->error = MPD_ERROR_NOTMPD; +++ return 1; +++ } +++ tmp = ++test; +++ } +++ +++ return 0; +++} +++ +++mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) { +++ int err; +++ char * rt; +++ char * output = NULL; +++ mpd_Connection * connection = (mpd_Connection *)malloc(sizeof(mpd_Connection)); +++ struct timeval tv; +++ fd_set fds; +++ strcpy(connection->buffer,""); +++ connection->buflen = 0; +++ connection->bufstart = 0; +++ strcpy(connection->errorStr,""); +++ connection->error = 0; +++ connection->doneProcessing = 0; +++ connection->commandList = 0; +++ connection->listOks = 0; +++ connection->doneListOk = 0; +++ connection->returnElement = NULL; +++ connection->request = NULL; +++ +++ if (winsock_dll_error(connection)) +++ return connection; +++ +++ if (mpd_connect(connection, host, port, timeout) < 0) +++ return connection; +++ +++ while(!(rt = strstr(connection->buffer,"\n"))) { +++ tv.tv_sec = connection->timeout.tv_sec; +++ tv.tv_usec = connection->timeout.tv_usec; +++ FD_ZERO(&fds); +++ FD_SET(connection->sock,&fds); +++ if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) { +++ int readed; +++ readed = recv(connection->sock, +++ &(connection->buffer[connection->buflen]), +++ MPD_BUFFER_MAX_LENGTH-connection->buflen,0); +++ if(readed<=0) { +++ snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH, +++ "problems getting a response from" +++ " \"%s\" on port %i : %s",host, +++ port, strerror(errno)); +++ connection->error = MPD_ERROR_NORESPONSE; +++ return connection; +++ } +++ connection->buflen+=readed; +++ connection->buffer[connection->buflen] = '\0'; +++ } +++ else if(err<0) { +++ if (SELECT_ERRNO_IGNORE) +++ continue; +++ snprintf(connection->errorStr, +++ MPD_ERRORSTR_MAX_LENGTH, +++ "problems connecting to \"%s\" on port" +++ " %i",host,port); +++ connection->error = MPD_ERROR_CONNPORT; +++ return connection; +++ } +++ else { +++ snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH, +++ "timeout in attempting to get a response from" +++ " \"%s\" on port %i",host,port); +++ connection->error = MPD_ERROR_NORESPONSE; +++ return connection; +++ } +++ } +++ +++ *rt = '\0'; +++ output = strdup(connection->buffer); +++ strcpy(connection->buffer,rt+1); +++ connection->buflen = strlen(connection->buffer); +++ +++ if(mpd_parseWelcome(connection,host,port,rt,output) == 0) connection->doneProcessing = 1; +++ +++ free(output); +++ +++ return connection; +++} +++ +++void mpd_clearError(mpd_Connection * connection) { +++ connection->error = 0; +++ connection->errorStr[0] = '\0'; +++} +++ +++void mpd_closeConnection(mpd_Connection * connection) { +++ closesocket(connection->sock); +++ if(connection->returnElement) free(connection->returnElement); +++ if(connection->request) free(connection->request); +++ free(connection); +++ WSACleanup(); +++} +++ +++static void mpd_executeCommand(mpd_Connection * connection, char * command) { +++ int ret; +++ struct timeval tv; +++ fd_set fds; +++ char * commandPtr = command; +++ int commandLen = strlen(command); +++ +++ if(!connection->doneProcessing && !connection->commandList) { +++ strcpy(connection->errorStr,"not done processing current command"); +++ connection->error = 1; +++ return; +++ } +++ +++ mpd_clearError(connection); +++ +++ FD_ZERO(&fds); +++ FD_SET(connection->sock,&fds); +++ tv.tv_sec = connection->timeout.tv_sec; +++ tv.tv_usec = connection->timeout.tv_usec; +++ +++ while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) || +++ (ret==-1 && SELECT_ERRNO_IGNORE)) { +++ ret = send(connection->sock,commandPtr,commandLen,MSG_DONTWAIT); +++ if(ret<=0) +++ { +++ if (SENDRECV_ERRNO_IGNORE) continue; +++ snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH, +++ "problems giving command \"%s\"",command); +++ connection->error = MPD_ERROR_SENDING; +++ return; +++ } +++ else { +++ commandPtr+=ret; +++ commandLen-=ret; +++ } +++ +++ if(commandLen<=0) break; +++ } +++ +++ if(commandLen>0) { +++ perror(""); +++ snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH, +++ "timeout sending command \"%s\"",command); +++ connection->error = MPD_ERROR_TIMEOUT; +++ return; +++ } +++ +++ if(!connection->commandList) connection->doneProcessing = 0; +++ else if(connection->commandList == COMMAND_LIST_OK) { +++ connection->listOks++; +++ } +++} +++ +++static void mpd_getNextReturnElement(mpd_Connection * connection) { +++ char * output = NULL; +++ char * rt = NULL; +++ char * name = NULL; +++ char * value = NULL; +++ fd_set fds; +++ struct timeval tv; +++ char * tok = NULL; +++ int readed; +++ char * bufferCheck = NULL; +++ int err; +++ int pos; +++ +++ if(connection->returnElement) mpd_freeReturnElement(connection->returnElement); +++ connection->returnElement = NULL; +++ +++ if(connection->doneProcessing || (connection->listOks && +++ connection->doneListOk)) +++ { +++ strcpy(connection->errorStr,"already done processing current command"); +++ connection->error = 1; +++ return; +++ } +++ +++ bufferCheck = connection->buffer+connection->bufstart; +++ while(connection->bufstart>=connection->buflen || +++ !(rt = strchr(bufferCheck,'\n'))) { +++ if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) { +++ memmove(connection->buffer, +++ connection->buffer+ +++ connection->bufstart, +++ connection->buflen- +++ connection->bufstart+1); +++ connection->buflen-=connection->bufstart; +++ connection->bufstart = 0; +++ } +++ if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) { +++ strcpy(connection->errorStr,"buffer overrun"); +++ connection->error = MPD_ERROR_BUFFEROVERRUN; +++ connection->doneProcessing = 1; +++ connection->doneListOk = 0; +++ return; +++ } +++ bufferCheck = connection->buffer+connection->buflen; +++ tv.tv_sec = connection->timeout.tv_sec; +++ tv.tv_usec = connection->timeout.tv_usec; +++ FD_ZERO(&fds); +++ FD_SET(connection->sock,&fds); +++ if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) { +++ readed = recv(connection->sock, +++ connection->buffer+connection->buflen, +++ MPD_BUFFER_MAX_LENGTH-connection->buflen, +++ MSG_DONTWAIT); +++ if(readed<0 && SENDRECV_ERRNO_IGNORE) { +++ continue; +++ } +++ if(readed<=0) { +++ strcpy(connection->errorStr,"connection" +++ " closed"); +++ connection->error = MPD_ERROR_CONNCLOSED; +++ connection->doneProcessing = 1; +++ connection->doneListOk = 0; +++ return; +++ } +++ connection->buflen+=readed; +++ connection->buffer[connection->buflen] = '\0'; +++ } +++ else if(err<0 && SELECT_ERRNO_IGNORE) continue; +++ else { +++ strcpy(connection->errorStr,"connection timeout"); +++ connection->error = MPD_ERROR_TIMEOUT; +++ connection->doneProcessing = 1; +++ connection->doneListOk = 0; +++ return; +++ } +++ } +++ +++ *rt = '\0'; +++ output = connection->buffer+connection->bufstart; +++ connection->bufstart = rt - connection->buffer + 1; +++ +++ if(strcmp(output,"OK")==0) { +++ if(connection->listOks > 0) { +++ strcpy(connection->errorStr, "expected more list_OK's"); +++ connection->error = 1; +++ } +++ connection->listOks = 0; +++ connection->doneProcessing = 1; +++ connection->doneListOk = 0; +++ return; +++ } +++ +++ if(strcmp(output, "list_OK") == 0) { +++ if(!connection->listOks) { +++ strcpy(connection->errorStr, +++ "got an unexpected list_OK"); +++ connection->error = 1; +++ } +++ else { +++ connection->doneListOk = 1; +++ connection->listOks--; +++ } +++ return; +++ } +++ +++ if(strncmp(output,"ACK",strlen("ACK"))==0) { +++ char * test; +++ char * needle; +++ int val; +++ +++ strcpy(connection->errorStr, output); +++ connection->error = MPD_ERROR_ACK; +++ connection->errorCode = MPD_ACK_ERROR_UNK; +++ connection->errorAt = MPD_ERROR_AT_UNK; +++ connection->doneProcessing = 1; +++ connection->doneListOk = 0; +++ +++ needle = strchr(output, '['); +++ if(!needle) return; +++ val = strtol(needle+1, &test, 10); +++ if(*test != '@') return; +++ connection->errorCode = val; +++ val = strtol(test+1, &test, 10); +++ if(*test != ']') return; +++ connection->errorAt = val; +++ return; +++ } +++ +++ tok = strchr(output, ':'); +++ if (!tok) return; +++ pos = tok - output; +++ value = ++tok; +++ name = output; +++ name[pos] = '\0'; +++ +++ if(value[0]==' ') { +++ connection->returnElement = mpd_newReturnElement(name,&(value[1])); +++ } +++ else { +++ snprintf(connection->errorStr,MPD_ERRORSTR_MAX_LENGTH, +++ "error parsing: %s:%s",name,value); +++ connection->error = 1; +++ } +++} +++ +++void mpd_finishCommand(mpd_Connection * connection) { +++ while(!connection->doneProcessing) { +++ if(connection->doneListOk) connection->doneListOk = 0; +++ mpd_getNextReturnElement(connection); +++ } +++} +++ +++static void mpd_finishListOkCommand(mpd_Connection * connection) { +++ while(!connection->doneProcessing && connection->listOks && +++ !connection->doneListOk) +++ { +++ mpd_getNextReturnElement(connection); +++ } +++} +++ +++int mpd_nextListOkCommand(mpd_Connection * connection) { +++ mpd_finishListOkCommand(connection); +++ if(!connection->doneProcessing) connection->doneListOk = 0; +++ if(connection->listOks == 0 || connection->doneProcessing) return -1; +++ return 0; +++} +++ +++void mpd_sendStatusCommand(mpd_Connection * connection) { +++ mpd_executeCommand(connection,"status\n"); +++} +++ +++mpd_Status * mpd_getStatus(mpd_Connection * connection) { +++ mpd_Status * status; +++ +++ /*mpd_executeCommand(connection,"status\n"); +++ +++ if(connection->error) return NULL;*/ +++ +++ if(connection->doneProcessing || (connection->listOks && +++ connection->doneListOk)) +++ { +++ return NULL; +++ } +++ +++ if(!connection->returnElement) mpd_getNextReturnElement(connection); +++ +++ status = (mpd_Status *)malloc(sizeof(mpd_Status)); +++ status->volume = -1; +++ status->repeat = 0; +++ status->random = 0; +++ status->playlist = -1; +++ status->playlistLength = -1; +++ status->state = -1; +++ status->song = 0; +++ status->songid = 0; +++ status->elapsedTime = 0; +++ status->totalTime = 0; +++ status->bitRate = 0; +++ status->sampleRate = 0; +++ status->bits = 0; +++ status->channels = 0; +++ status->crossfade = -1; +++ status->error = NULL; +++ status->updatingDb = 0; +++ +++ if(connection->error) { +++ free(status); +++ return NULL; +++ } +++ while(connection->returnElement) { +++ mpd_ReturnElement * re = connection->returnElement; +++ if(strcmp(re->name,"volume")==0) { +++ status->volume = atoi(re->value); +++ } +++ else if(strcmp(re->name,"repeat")==0) { +++ status->repeat = atoi(re->value); +++ } +++ else if(strcmp(re->name,"random")==0) { +++ status->random = atoi(re->value); +++ } +++ else if(strcmp(re->name,"playlist")==0) { +++ status->playlist = strtol(re->value,NULL,10); +++ } +++ else if(strcmp(re->name,"playlistlength")==0) { +++ status->playlistLength = atoi(re->value); +++ } +++ else if(strcmp(re->name,"bitrate")==0) { +++ status->bitRate = atoi(re->value); +++ } +++ else if(strcmp(re->name,"state")==0) { +++ if(strcmp(re->value,"play")==0) { +++ status->state = MPD_STATUS_STATE_PLAY; +++ } +++ else if(strcmp(re->value,"stop")==0) { +++ status->state = MPD_STATUS_STATE_STOP; +++ } +++ else if(strcmp(re->value,"pause")==0) { +++ status->state = MPD_STATUS_STATE_PAUSE; +++ } +++ else { +++ status->state = MPD_STATUS_STATE_UNKNOWN; +++ } +++ } +++ else if(strcmp(re->name,"song")==0) { +++ status->song = atoi(re->value); +++ } +++ else if(strcmp(re->name,"songid")==0) { +++ status->songid = atoi(re->value); +++ } +++ else if(strcmp(re->name,"time")==0) { +++ char * tok = strchr(re->value,':'); +++ /* the second strchr below is a safety check */ +++ if (tok && (strchr(tok,0) > (tok+1))) { +++ /* atoi stops at the first non-[0-9] char: */ +++ status->elapsedTime = atoi(re->value); +++ status->totalTime = atoi(tok+1); +++ } +++ } +++ else if(strcmp(re->name,"error")==0) { +++ status->error = strdup(re->value); +++ } +++ else if(strcmp(re->name,"xfade")==0) { +++ status->crossfade = atoi(re->value); +++ } +++ else if(strcmp(re->name,"updating_db")==0) { +++ status->updatingDb = atoi(re->value); +++ } +++ else if(strcmp(re->name,"audio")==0) { +++ char * tok = strchr(re->value,':'); +++ if (tok && (strchr(tok,0) > (tok+1))) { +++ status->sampleRate = atoi(re->value); +++ status->bits = atoi(++tok); +++ tok = strchr(tok,':'); +++ if (tok && (strchr(tok,0) > (tok+1))) +++ status->channels = atoi(tok+1); +++ } +++ } +++ +++ mpd_getNextReturnElement(connection); +++ if(connection->error) { +++ free(status); +++ return NULL; +++ } +++ } +++ +++ if(connection->error) { +++ free(status); +++ return NULL; +++ } +++ else if(status->state<0) { +++ strcpy(connection->errorStr,"state not found"); +++ connection->error = 1; +++ free(status); +++ return NULL; +++ } +++ +++ return status; +++} +++ +++void mpd_freeStatus(mpd_Status * status) { +++ if(status->error) free(status->error); +++ free(status); +++} +++ +++void mpd_sendStatsCommand(mpd_Connection * connection) { +++ mpd_executeCommand(connection,"stats\n"); +++} +++ +++mpd_Stats * mpd_getStats(mpd_Connection * connection) { +++ mpd_Stats * stats; +++ +++ /*mpd_executeCommand(connection,"stats\n"); +++ +++ if(connection->error) return NULL;*/ +++ +++ if(connection->doneProcessing || (connection->listOks && +++ connection->doneListOk)) +++ { +++ return NULL; +++ } +++ +++ if(!connection->returnElement) mpd_getNextReturnElement(connection); +++ +++ stats = (mpd_Stats *)malloc(sizeof(mpd_Stats)); +++ stats->numberOfArtists = 0; +++ stats->numberOfAlbums = 0; +++ stats->numberOfSongs = 0; +++ stats->uptime = 0; +++ stats->dbUpdateTime = 0; +++ stats->playTime = 0; +++ stats->dbPlayTime = 0; +++ +++ if(connection->error) { +++ free(stats); +++ return NULL; +++ } +++ while(connection->returnElement) { +++ mpd_ReturnElement * re = connection->returnElement; +++ if(strcmp(re->name,"artists")==0) { +++ stats->numberOfArtists = atoi(re->value); +++ } +++ else if(strcmp(re->name,"albums")==0) { +++ stats->numberOfAlbums = atoi(re->value); +++ } +++ else if(strcmp(re->name,"songs")==0) { +++ stats->numberOfSongs = atoi(re->value); +++ } +++ else if(strcmp(re->name,"uptime")==0) { +++ stats->uptime = strtol(re->value,NULL,10); +++ } +++ else if(strcmp(re->name,"db_update")==0) { +++ stats->dbUpdateTime = strtol(re->value,NULL,10); +++ } +++ else if(strcmp(re->name,"playtime")==0) { +++ stats->playTime = strtol(re->value,NULL,10); +++ } +++ else if(strcmp(re->name,"db_playtime")==0) { +++ stats->dbPlayTime = strtol(re->value,NULL,10); +++ } +++ +++ mpd_getNextReturnElement(connection); +++ if(connection->error) { +++ free(stats); +++ return NULL; +++ } +++ } +++ +++ if(connection->error) { +++ free(stats); +++ return NULL; +++ } +++ +++ return stats; +++} +++ +++void mpd_freeStats(mpd_Stats * stats) { +++ free(stats); +++} +++ +++mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection) +++{ +++ mpd_SearchStats * stats; +++ mpd_ReturnElement * re; +++ +++ if (connection->doneProcessing || +++ (connection->listOks && connection->doneListOk)) { +++ return NULL; +++ } +++ +++ if (!connection->returnElement) mpd_getNextReturnElement(connection); +++ +++ if (connection->error) +++ return NULL; +++ +++ stats = (mpd_SearchStats *)malloc(sizeof(mpd_SearchStats)); +++ stats->numberOfSongs = 0; +++ stats->playTime = 0; +++ +++ while (connection->returnElement) { +++ re = connection->returnElement; +++ +++ if (strcmp(re->name, "songs") == 0) { +++ stats->numberOfSongs = atoi(re->value); +++ } else if (strcmp(re->name, "playtime") == 0) { +++ stats->playTime = strtol(re->value, NULL, 10); +++ } +++ +++ mpd_getNextReturnElement(connection); +++ if (connection->error) { +++ free(stats); +++ return NULL; +++ } +++ } +++ +++ if (connection->error) { +++ free(stats); +++ return NULL; +++ } +++ +++ return stats; +++} +++ +++void mpd_freeSearchStats(mpd_SearchStats * stats) +++{ +++ free(stats); +++} +++ +++static void mpd_initSong(mpd_Song * song) { +++ song->file = NULL; +++ song->artist = NULL; +++ song->album = NULL; +++ song->track = NULL; +++ song->title = NULL; +++ song->name = NULL; +++ song->date = NULL; +++ /* added by Qball */ +++ song->genre = NULL; +++ song->composer = NULL; +++ song->performer = NULL; +++ song->disc = NULL; +++ song->comment = NULL; +++ +++ song->time = MPD_SONG_NO_TIME; +++ song->pos = MPD_SONG_NO_NUM; +++ song->id = MPD_SONG_NO_ID; +++} +++ +++static void mpd_finishSong(mpd_Song * song) { +++ if(song->file) free(song->file); +++ if(song->artist) free(song->artist); +++ if(song->album) free(song->album); +++ if(song->title) free(song->title); +++ if(song->track) free(song->track); +++ if(song->name) free(song->name); +++ if(song->date) free(song->date); +++ if(song->genre) free(song->genre); +++ if(song->composer) free(song->composer); +++ if(song->disc) free(song->disc); +++ if(song->comment) free(song->comment); +++} +++ +++mpd_Song * mpd_newSong(void) { +++ mpd_Song * ret = (mpd_Song *)malloc(sizeof(mpd_Song)); +++ +++ mpd_initSong(ret); +++ +++ return ret; +++} +++ +++void mpd_freeSong(mpd_Song * song) { +++ mpd_finishSong(song); +++ free(song); +++} +++ +++mpd_Song * mpd_songDup(mpd_Song * song) { +++ mpd_Song * ret = mpd_newSong(); +++ +++ if(song->file) ret->file = strdup(song->file); +++ if(song->artist) ret->artist = strdup(song->artist); +++ if(song->album) ret->album = strdup(song->album); +++ if(song->title) ret->title = strdup(song->title); +++ if(song->track) ret->track = strdup(song->track); +++ if(song->name) ret->name = strdup(song->name); +++ if(song->date) ret->date = strdup(song->date); +++ if(song->genre) ret->genre= strdup(song->genre); +++ if(song->composer) ret->composer= strdup(song->composer); +++ if(song->disc) ret->disc = strdup(song->disc); +++ if(song->comment) ret->comment = strdup(song->comment); +++ ret->time = song->time; +++ ret->pos = song->pos; +++ ret->id = song->id; +++ +++ return ret; +++} +++ +++static void mpd_initDirectory(mpd_Directory * directory) { +++ directory->path = NULL; +++} +++ +++static void mpd_finishDirectory(mpd_Directory * directory) { +++ if(directory->path) free(directory->path); +++} +++ +++mpd_Directory * mpd_newDirectory(void) { +++ mpd_Directory * directory = (mpd_Directory *)malloc(sizeof(mpd_Directory));; +++ +++ mpd_initDirectory(directory); +++ +++ return directory; +++} +++ +++void mpd_freeDirectory(mpd_Directory * directory) { +++ mpd_finishDirectory(directory); +++ +++ free(directory); +++} +++ +++mpd_Directory * mpd_directoryDup(mpd_Directory * directory) { +++ mpd_Directory * ret = mpd_newDirectory(); +++ +++ if(directory->path) ret->path = strdup(directory->path); +++ +++ return ret; +++} +++ +++static void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) { +++ playlist->path = NULL; +++} +++ +++static void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) { +++ if(playlist->path) free(playlist->path); +++} +++ +++mpd_PlaylistFile * mpd_newPlaylistFile(void) { +++ mpd_PlaylistFile * playlist = (mpd_PlaylistFile *)malloc(sizeof(mpd_PlaylistFile)); +++ +++ mpd_initPlaylistFile(playlist); +++ +++ return playlist; +++} +++ +++void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) { +++ mpd_finishPlaylistFile(playlist); +++ free(playlist); +++} +++ +++mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) { +++ mpd_PlaylistFile * ret = mpd_newPlaylistFile(); +++ +++ if(playlist->path) ret->path = strdup(playlist->path); +++ +++ return ret; +++} +++ +++static void mpd_initInfoEntity(mpd_InfoEntity * entity) { +++ entity->info.directory = NULL; +++} +++ +++static void mpd_finishInfoEntity(mpd_InfoEntity * entity) { +++ if(entity->info.directory) { +++ if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) { +++ mpd_freeDirectory(entity->info.directory); +++ } +++ else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) { +++ mpd_freeSong(entity->info.song); +++ } +++ else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) { +++ mpd_freePlaylistFile(entity->info.playlistFile); +++ } +++ } +++} +++ +++mpd_InfoEntity * mpd_newInfoEntity(void) { +++ mpd_InfoEntity * entity = (mpd_InfoEntity *)malloc(sizeof(mpd_InfoEntity)); +++ +++ mpd_initInfoEntity(entity); +++ +++ return entity; +++} +++ +++void mpd_freeInfoEntity(mpd_InfoEntity * entity) { +++ mpd_finishInfoEntity(entity); +++ free(entity); +++} +++ +++static void mpd_sendInfoCommand(mpd_Connection * connection, char * command) { +++ mpd_executeCommand(connection,command); +++} +++ +++mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) { +++ mpd_InfoEntity * entity = NULL; +++ +++ if(connection->doneProcessing || (connection->listOks && +++ connection->doneListOk)) +++ { +++ return NULL; +++ } +++ +++ if(!connection->returnElement) mpd_getNextReturnElement(connection); +++ +++ if(connection->returnElement) { +++ if(strcmp(connection->returnElement->name,"file")==0) { +++ entity = mpd_newInfoEntity(); +++ entity->type = MPD_INFO_ENTITY_TYPE_SONG; +++ entity->info.song = mpd_newSong(); +++ entity->info.song->file = +++ strdup(connection->returnElement->value); +++ } +++ else if(strcmp(connection->returnElement->name, +++ "directory")==0) { +++ entity = mpd_newInfoEntity(); +++ entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY; +++ entity->info.directory = mpd_newDirectory(); +++ entity->info.directory->path = +++ strdup(connection->returnElement->value); +++ } +++ else if(strcmp(connection->returnElement->name,"playlist")==0) { +++ entity = mpd_newInfoEntity(); +++ entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE; +++ entity->info.playlistFile = mpd_newPlaylistFile(); +++ entity->info.playlistFile->path = +++ strdup(connection->returnElement->value); +++ } +++ else if(strcmp(connection->returnElement->name, "cpos") == 0){ +++ entity = mpd_newInfoEntity(); +++ entity->type = MPD_INFO_ENTITY_TYPE_SONG; +++ entity->info.song = mpd_newSong(); +++ entity->info.song->pos = atoi(connection->returnElement->value); +++ } +++ else { +++ connection->error = 1; +++ strcpy(connection->errorStr,"problem parsing song info"); +++ return NULL; +++ } +++ } +++ else return NULL; +++ +++ mpd_getNextReturnElement(connection); +++ while(connection->returnElement) { +++ mpd_ReturnElement * re = connection->returnElement; +++ +++ if(strcmp(re->name,"file")==0) return entity; +++ else if(strcmp(re->name,"directory")==0) return entity; +++ else if(strcmp(re->name,"playlist")==0) return entity; +++ else if(strcmp(re->name,"cpos")==0) return entity; +++ +++ if(entity->type == MPD_INFO_ENTITY_TYPE_SONG && +++ strlen(re->value)) { +++ if(!entity->info.song->artist && +++ strcmp(re->name,"Artist")==0) { +++ entity->info.song->artist = strdup(re->value); +++ } +++ else if(!entity->info.song->album && +++ strcmp(re->name,"Album")==0) { +++ entity->info.song->album = strdup(re->value); +++ } +++ else if(!entity->info.song->title && +++ strcmp(re->name,"Title")==0) { +++ entity->info.song->title = strdup(re->value); +++ } +++ else if(!entity->info.song->track && +++ strcmp(re->name,"Track")==0) { +++ entity->info.song->track = strdup(re->value); +++ } +++ else if(!entity->info.song->name && +++ strcmp(re->name,"Name")==0) { +++ entity->info.song->name = strdup(re->value); +++ } +++ else if(entity->info.song->time==MPD_SONG_NO_TIME && +++ strcmp(re->name,"Time")==0) { +++ entity->info.song->time = atoi(re->value); +++ } +++ else if(entity->info.song->pos==MPD_SONG_NO_NUM && +++ strcmp(re->name,"Pos")==0) { +++ entity->info.song->pos = atoi(re->value); +++ } +++ else if(entity->info.song->id==MPD_SONG_NO_ID && +++ strcmp(re->name,"Id")==0) { +++ entity->info.song->id = atoi(re->value); +++ } +++ else if(!entity->info.song->date && +++ strcmp(re->name, "Date") == 0) { +++ entity->info.song->date = strdup(re->value); +++ } +++ else if(!entity->info.song->genre && +++ strcmp(re->name, "Genre") == 0) { +++ entity->info.song->genre = strdup(re->value); +++ } +++ else if(!entity->info.song->composer && +++ strcmp(re->name, "Composer") == 0) { +++ entity->info.song->composer = strdup(re->value); +++ } +++ else if(!entity->info.song->performer && +++ strcmp(re->name, "Performer") == 0) { +++ entity->info.song->performer = strdup(re->value); +++ } +++ else if(!entity->info.song->disc && +++ strcmp(re->name, "Disc") == 0) { +++ entity->info.song->disc = strdup(re->value); +++ } +++ else if(!entity->info.song->comment && +++ strcmp(re->name, "Comment") == 0) { +++ entity->info.song->comment = strdup(re->value); +++ } +++ } +++ else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) { +++ } +++ else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) { +++ } +++ +++ mpd_getNextReturnElement(connection); +++ } +++ +++ return entity; +++} +++ +++static char * mpd_getNextReturnElementNamed(mpd_Connection * connection, +++ const char * name) +++{ +++ if(connection->doneProcessing || (connection->listOks && +++ connection->doneListOk)) +++ { +++ return NULL; +++ } +++ +++ mpd_getNextReturnElement(connection); +++ while(connection->returnElement) { +++ mpd_ReturnElement * re = connection->returnElement; +++ +++ if(strcmp(re->name,name)==0) return strdup(re->value); +++ mpd_getNextReturnElement(connection); +++ } +++ +++ return NULL; +++} +++ +++char *mpd_getNextTag(mpd_Connection *connection, int type) +++{ +++ if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES || +++ type == MPD_TAG_ITEM_ANY) +++ return NULL; +++ if (type == MPD_TAG_ITEM_FILENAME) +++ return mpd_getNextReturnElementNamed(connection, "file"); +++ return mpd_getNextReturnElementNamed(connection, mpdTagItemKeys[type]); +++} +++ +++char * mpd_getNextArtist(mpd_Connection * connection) { +++ return mpd_getNextReturnElementNamed(connection,"Artist"); +++} +++ +++char * mpd_getNextAlbum(mpd_Connection * connection) { +++ return mpd_getNextReturnElementNamed(connection,"Album"); +++} +++ +++void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songPos) { +++ int len = strlen("playlistinfo")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "playlistinfo \"%i\"\n", songPos); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int id) { +++ int len = strlen("playlistid")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "playlistid \"%i\"\n", id); +++ mpd_sendInfoCommand(connection, string); +++ free(string); +++} +++ +++void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist) { +++ int len = strlen("plchanges")+2+LONGLONGLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "plchanges \"%lld\"\n", playlist); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist) { +++ int len = strlen("plchangesposid")+2+LONGLONGLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "plchangesposid \"%lld\"\n", playlist); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) { +++ char * sDir = mpd_sanitizeArg(dir); +++ int len = strlen("listall")+2+strlen(sDir)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "listall \"%s\"\n", sDir); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++ free(sDir); +++} +++ +++void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) { +++ char * sDir = mpd_sanitizeArg(dir); +++ int len = strlen("listallinfo")+2+strlen(sDir)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "listallinfo \"%s\"\n", sDir); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++ free(sDir); +++} +++ +++void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) { +++ char * sDir = mpd_sanitizeArg(dir); +++ int len = strlen("lsinfo")+2+strlen(sDir)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "lsinfo \"%s\"\n", sDir); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++ free(sDir); +++} +++ +++void mpd_sendCurrentSongCommand(mpd_Connection * connection) { +++ mpd_executeCommand(connection,"currentsong\n"); +++} +++ +++void mpd_sendSearchCommand(mpd_Connection * connection, int table, +++ const char * str) +++{ +++ mpd_startSearch(connection, 0); +++ mpd_addConstraintSearch(connection, table, str); +++ mpd_commitSearch(connection); +++} +++ +++void mpd_sendFindCommand(mpd_Connection * connection, int table, +++ const char * str) +++{ +++ mpd_startSearch(connection, 1); +++ mpd_addConstraintSearch(connection, table, str); +++ mpd_commitSearch(connection); +++} +++ +++void mpd_sendListCommand(mpd_Connection * connection, int table, +++ const char * arg1) +++{ +++ char st[10]; +++ int len; +++ char *string; +++ if(table == MPD_TABLE_ARTIST) strcpy(st,"artist"); +++ else if(table == MPD_TABLE_ALBUM) strcpy(st,"album"); +++ else { +++ connection->error = 1; +++ strcpy(connection->errorStr,"unknown table for list"); +++ return; +++ } +++ if(arg1) { +++ char * sanitArg1 = mpd_sanitizeArg(arg1); +++ len = strlen("list")+1+strlen(sanitArg1)+2+strlen(st)+3; +++ string = (char *)malloc(len); +++ snprintf(string, len, "list %s \"%s\"\n", st, sanitArg1); +++ free(sanitArg1); +++ } +++ else { +++ len = strlen("list")+1+strlen(st)+2; +++ string = (char *)malloc(len); +++ snprintf(string, len, "list %s\n", st); +++ } +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendAddCommand(mpd_Connection * connection, const char * file) { +++ char * sFile = mpd_sanitizeArg(file); +++ int len = strlen("add")+2+strlen(sFile)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "add \"%s\"\n", sFile); +++ mpd_executeCommand(connection,string); +++ free(string); +++ free(sFile); +++} +++ +++int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file) +++{ +++ int retval = -1; +++ char *sFile = mpd_sanitizeArg(file); +++ int len = strlen("addid")+2+strlen(sFile)+3; +++ char *string = (char *)malloc(len); +++ +++ snprintf(string, len, "addid \"%s\"\n", sFile); +++ mpd_sendInfoCommand(connection, string); +++ free(string); +++ free(sFile); +++ +++ string = mpd_getNextReturnElementNamed(connection, "Id"); +++ if (string) { +++ retval = atoi(string); +++ free(string); +++ } +++ +++ return retval; +++} +++ +++void mpd_sendDeleteCommand(mpd_Connection * connection, int songPos) { +++ int len = strlen("delete")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "delete \"%i\"\n", songPos); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendDeleteIdCommand(mpd_Connection * connection, int id) { +++ int len = strlen("deleteid")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "deleteid \"%i\"\n", id); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) { +++ char * sName = mpd_sanitizeArg(name); +++ int len = strlen("save")+2+strlen(sName)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "save \"%s\"\n", sName); +++ mpd_executeCommand(connection,string); +++ free(string); +++ free(sName); +++} +++ +++void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) { +++ char * sName = mpd_sanitizeArg(name); +++ int len = strlen("load")+2+strlen(sName)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "load \"%s\"\n", sName); +++ mpd_executeCommand(connection,string); +++ free(string); +++ free(sName); +++} +++ +++void mpd_sendRmCommand(mpd_Connection * connection, const char * name) { +++ char * sName = mpd_sanitizeArg(name); +++ int len = strlen("rm")+2+strlen(sName)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "rm \"%s\"\n", sName); +++ mpd_executeCommand(connection,string); +++ free(string); +++ free(sName); +++} +++ +++void mpd_sendRenameCommand(mpd_Connection *connection, const char *from, +++ const char *to) +++{ +++ char *sFrom = mpd_sanitizeArg(from); +++ char *sTo = mpd_sanitizeArg(to); +++ int len = strlen("rename")+2+strlen(sFrom)+3+strlen(sTo)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "rename \"%s\" \"%s\"\n", sFrom, sTo); +++ mpd_executeCommand(connection, string); +++ free(string); +++ free(sFrom); +++ free(sTo); +++} +++ +++void mpd_sendShuffleCommand(mpd_Connection * connection) { +++ mpd_executeCommand(connection,"shuffle\n"); +++} +++ +++void mpd_sendClearCommand(mpd_Connection * connection) { +++ mpd_executeCommand(connection,"clear\n"); +++} +++ +++void mpd_sendPlayCommand(mpd_Connection * connection, int songPos) { +++ int len = strlen("play")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "play \"%i\"\n", songPos); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendPlayIdCommand(mpd_Connection * connection, int id) { +++ int len = strlen("playid")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "playid \"%i\"\n", id); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendStopCommand(mpd_Connection * connection) { +++ mpd_executeCommand(connection,"stop\n"); +++} +++ +++void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode) { +++ int len = strlen("pause")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "pause \"%i\"\n", pauseMode); +++ mpd_executeCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendNextCommand(mpd_Connection * connection) { +++ mpd_executeCommand(connection,"next\n"); +++} +++ +++void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) { +++ int len = strlen("move")+2+INTLEN+3+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "move \"%i\" \"%i\"\n", from, to); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendMoveIdCommand(mpd_Connection * connection, int id, int to) { +++ int len = strlen("moveid")+2+INTLEN+3+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "moveid \"%i\" \"%i\"\n", id, to); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) { +++ int len = strlen("swap")+2+INTLEN+3+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "swap \"%i\" \"%i\"\n", song1, song2); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendSwapIdCommand(mpd_Connection * connection, int id1, int id2) { +++ int len = strlen("swapid")+2+INTLEN+3+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "swapid \"%i\" \"%i\"\n", id1, id2); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) { +++ int len = strlen("seek")+2+INTLEN+3+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "seek \"%i\" \"%i\"\n", song, time); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendSeekIdCommand(mpd_Connection * connection, int id, int time) { +++ int len = strlen("seekid")+2+INTLEN+3+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "seekid \"%i\" \"%i\"\n", id, time); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendUpdateCommand(mpd_Connection * connection, char * path) { +++ char * sPath = mpd_sanitizeArg(path); +++ int len = strlen("update")+2+strlen(sPath)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "update \"%s\"\n", sPath); +++ mpd_sendInfoCommand(connection,string); +++ free(string); +++ free(sPath); +++} +++ +++int mpd_getUpdateId(mpd_Connection * connection) { +++ char * jobid; +++ int ret = 0; +++ +++ jobid = mpd_getNextReturnElementNamed(connection,"updating_db"); +++ if(jobid) { +++ ret = atoi(jobid); +++ free(jobid); +++ } +++ +++ return ret; +++} +++ +++void mpd_sendPrevCommand(mpd_Connection * connection) { +++ mpd_executeCommand(connection,"previous\n"); +++} +++ +++void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) { +++ int len = strlen("repeat")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "repeat \"%i\"\n", repeatMode); +++ mpd_executeCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) { +++ int len = strlen("random")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "random \"%i\"\n", randomMode); +++ mpd_executeCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange) { +++ int len = strlen("setvol")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "setvol \"%i\"\n", volumeChange); +++ mpd_executeCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) { +++ int len = strlen("volume")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "volume \"%i\"\n", volumeChange); +++ mpd_executeCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds) { +++ int len = strlen("crossfade")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "crossfade \"%i\"\n", seconds); +++ mpd_executeCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass) { +++ char * sPass = mpd_sanitizeArg(pass); +++ int len = strlen("password")+2+strlen(sPass)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "password \"%s\"\n", sPass); +++ mpd_executeCommand(connection,string); +++ free(string); +++ free(sPass); +++} +++ +++void mpd_sendCommandListBegin(mpd_Connection * connection) { +++ if(connection->commandList) { +++ strcpy(connection->errorStr,"already in command list mode"); +++ connection->error = 1; +++ return; +++ } +++ connection->commandList = COMMAND_LIST; +++ mpd_executeCommand(connection,"command_list_begin\n"); +++} +++ +++void mpd_sendCommandListOkBegin(mpd_Connection * connection) { +++ if(connection->commandList) { +++ strcpy(connection->errorStr,"already in command list mode"); +++ connection->error = 1; +++ return; +++ } +++ connection->commandList = COMMAND_LIST_OK; +++ mpd_executeCommand(connection,"command_list_ok_begin\n"); +++ connection->listOks = 0; +++} +++ +++void mpd_sendCommandListEnd(mpd_Connection * connection) { +++ if(!connection->commandList) { +++ strcpy(connection->errorStr,"not in command list mode"); +++ connection->error = 1; +++ return; +++ } +++ connection->commandList = 0; +++ mpd_executeCommand(connection,"command_list_end\n"); +++} +++ +++void mpd_sendOutputsCommand(mpd_Connection * connection) { +++ mpd_executeCommand(connection,"outputs\n"); +++} +++ +++mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection) { +++ mpd_OutputEntity * output = NULL; +++ +++ if(connection->doneProcessing || (connection->listOks && +++ connection->doneListOk)) +++ { +++ return NULL; +++ } +++ +++ if(connection->error) return NULL; +++ +++ output = (mpd_OutputEntity *)malloc(sizeof(mpd_OutputEntity)); +++ output->id = -10; +++ output->name = NULL; +++ output->enabled = 0; +++ +++ if(!connection->returnElement) mpd_getNextReturnElement(connection); +++ +++ while(connection->returnElement) { +++ mpd_ReturnElement * re = connection->returnElement; +++ if(strcmp(re->name,"outputid")==0) { +++ if(output!=NULL && output->id>=0) return output; +++ output->id = atoi(re->value); +++ } +++ else if(strcmp(re->name,"outputname")==0) { +++ output->name = strdup(re->value); +++ } +++ else if(strcmp(re->name,"outputenabled")==0) { +++ output->enabled = atoi(re->value); +++ } +++ +++ mpd_getNextReturnElement(connection); +++ if(connection->error) { +++ free(output); +++ return NULL; +++ } +++ +++ } +++ +++ return output; +++} +++ +++void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId) { +++ int len = strlen("enableoutput")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "enableoutput \"%i\"\n", outputId); +++ mpd_executeCommand(connection,string); +++ free(string); +++} +++ +++void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId) { +++ int len = strlen("disableoutput")+2+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "disableoutput \"%i\"\n", outputId); +++ mpd_executeCommand(connection,string); +++ free(string); +++} +++ +++void mpd_freeOutputElement(mpd_OutputEntity * output) { +++ free(output->name); +++ free(output); +++} +++ +++/** +++ * mpd_sendNotCommandsCommand +++ * odd naming, but it gets the not allowed commands +++ */ +++ +++void mpd_sendNotCommandsCommand(mpd_Connection * connection) +++{ +++ mpd_executeCommand(connection, "notcommands\n"); +++} +++ +++/** +++ * mpd_sendCommandsCommand +++ * odd naming, but it gets the allowed commands +++ */ +++void mpd_sendCommandsCommand(mpd_Connection * connection) +++{ +++ mpd_executeCommand(connection, "commands\n"); +++} +++ +++/** +++ * Get the next returned command +++ */ +++char * mpd_getNextCommand(mpd_Connection * connection) +++{ +++ return mpd_getNextReturnElementNamed(connection, "command"); +++} +++ +++void mpd_sendUrlHandlersCommand(mpd_Connection * connection) +++{ +++ mpd_executeCommand(connection, "urlhandlers\n"); +++} +++ +++char * mpd_getNextHandler(mpd_Connection * connection) +++{ +++ return mpd_getNextReturnElementNamed(connection, "handler"); +++} +++ +++void mpd_sendTagTypesCommand(mpd_Connection * connection) +++{ +++ mpd_executeCommand(connection, "tagtypes\n"); +++} +++ +++char * mpd_getNextTagType(mpd_Connection * connection) +++{ +++ return mpd_getNextReturnElementNamed(connection, "tagtype"); +++} +++ +++void mpd_startSearch(mpd_Connection *connection, int exact) +++{ +++ if (connection->request) { +++ strcpy(connection->errorStr, "search already in progress"); +++ connection->error = 1; +++ return; +++ } +++ +++ if (exact) connection->request = strdup("find"); +++ else connection->request = strdup("search"); +++} +++ +++void mpd_startStatsSearch(mpd_Connection *connection) +++{ +++ if (connection->request) { +++ strcpy(connection->errorStr, "search already in progress"); +++ connection->error = 1; +++ return; +++ } +++ +++ connection->request = strdup("count"); +++} +++ +++void mpd_startPlaylistSearch(mpd_Connection *connection, int exact) +++{ +++ if (connection->request) { +++ strcpy(connection->errorStr, "search already in progress"); +++ connection->error = 1; +++ return; +++ } +++ +++ if (exact) connection->request = strdup("playlistfind"); +++ else connection->request = strdup("playlistsearch"); +++} +++ +++void mpd_startFieldSearch(mpd_Connection *connection, int type) +++{ +++ char *strtype; +++ int len; +++ +++ if (connection->request) { +++ strcpy(connection->errorStr, "search already in progress"); +++ connection->error = 1; +++ return; +++ } +++ +++ if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) { +++ strcpy(connection->errorStr, "invalid type specified"); +++ connection->error = 1; +++ return; +++ } +++ +++ strtype = mpdTagItemKeys[type]; +++ +++ len = 5+strlen(strtype)+1; +++ connection->request = (char *)malloc(len); +++ +++ snprintf(connection->request, len, "list %c%s", +++ tolower(strtype[0]), strtype+1); +++} +++ +++void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name) +++{ +++ char *strtype; +++ char *arg; +++ int len; +++ char *string; +++ +++ if (!connection->request) { +++ strcpy(connection->errorStr, "no search in progress"); +++ connection->error = 1; +++ return; +++ } +++ +++ if (type < 0 || type >= MPD_TAG_NUM_OF_ITEM_TYPES) { +++ strcpy(connection->errorStr, "invalid type specified"); +++ connection->error = 1; +++ return; +++ } +++ +++ if (name == NULL) { +++ strcpy(connection->errorStr, "no name specified"); +++ connection->error = 1; +++ return; +++ } +++ +++ string = strdup(connection->request); +++ strtype = mpdTagItemKeys[type]; +++ arg = mpd_sanitizeArg(name); +++ +++ len = strlen(string)+1+strlen(strtype)+2+strlen(arg)+2; +++ connection->request = (char *)realloc(connection->request, len); +++ snprintf(connection->request, len, "%s %c%s \"%s\"", +++ string, tolower(strtype[0]), strtype+1, arg); +++ +++ free(string); +++ free(arg); +++} +++ +++void mpd_commitSearch(mpd_Connection *connection) +++{ +++ int len; +++ +++ if (!connection->request) { +++ strcpy(connection->errorStr, "no search in progress"); +++ connection->error = 1; +++ return; +++ } +++ +++ len = strlen(connection->request)+2; +++ connection->request = (char *)realloc(connection->request, len); +++ connection->request[len-2] = '\n'; +++ connection->request[len-1] = '\0'; +++ mpd_sendInfoCommand(connection, connection->request); +++ +++ free(connection->request); +++ connection->request = NULL; +++} +++ +++/** +++ * @param connection a MpdConnection +++ * @param path the path to the playlist. +++ * +++ * List the content, with full metadata, of a stored playlist. +++ * +++ */ +++void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path) +++{ +++ char *arg = mpd_sanitizeArg(path); +++ int len = strlen("listplaylistinfo")+2+strlen(arg)+3; +++ char *query = (char *)malloc(len); +++ snprintf(query, len, "listplaylistinfo \"%s\"\n", arg); +++ mpd_sendInfoCommand(connection, query); +++ free(arg); +++ free(query); +++} +++ +++/** +++ * @param connection a MpdConnection +++ * @param path the path to the playlist. +++ * +++ * List the content of a stored playlist. +++ * +++ */ +++void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path) +++{ +++ char *arg = mpd_sanitizeArg(path); +++ int len = strlen("listplaylist")+2+strlen(arg)+3; +++ char *query = (char *)malloc(len); +++ snprintf(query, len, "listplaylist \"%s\"\n", arg); +++ mpd_sendInfoCommand(connection, query); +++ free(arg); +++ free(query); +++} +++ +++void mpd_sendPlaylistClearCommand(mpd_Connection *connection, char *path) +++{ +++ char *sPath = mpd_sanitizeArg(path); +++ int len = strlen("playlistclear")+2+strlen(sPath)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "playlistclear \"%s\"\n", sPath); +++ mpd_executeCommand(connection, string); +++ free(sPath); +++ free(string); +++} +++ +++void mpd_sendPlaylistAddCommand(mpd_Connection *connection, +++ char *playlist, char *path) +++{ +++ char *sPlaylist = mpd_sanitizeArg(playlist); +++ char *sPath = mpd_sanitizeArg(path); +++ int len = strlen("playlistadd")+2+strlen(sPlaylist)+3+strlen(sPath)+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "playlistadd \"%s\" \"%s\"\n", sPlaylist, sPath); +++ mpd_executeCommand(connection, string); +++ free(sPlaylist); +++ free(sPath); +++ free(string); +++} +++ +++void mpd_sendPlaylistMoveCommand(mpd_Connection *connection, +++ char *playlist, int from, int to) +++{ +++ char *sPlaylist = mpd_sanitizeArg(playlist); +++ int len = strlen("playlistmove")+ +++ 2+strlen(sPlaylist)+3+INTLEN+3+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "playlistmove \"%s\" \"%i\" \"%i\"\n", +++ sPlaylist, from, to); +++ mpd_executeCommand(connection, string); +++ free(sPlaylist); +++ free(string); +++} +++ +++void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection, +++ char *playlist, int pos) +++{ +++ char *sPlaylist = mpd_sanitizeArg(playlist); +++ int len = strlen("playlistdelete")+2+strlen(sPlaylist)+3+INTLEN+3; +++ char *string = (char *)malloc(len); +++ snprintf(string, len, "playlistdelete \"%s\" \"%i\"\n", sPlaylist, pos); +++ mpd_executeCommand(connection, string); +++ free(sPlaylist); +++ free(string); +++} ++diff -r 171db9560cb5 clients/mpd/libmpdclient.h ++--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++++ b/clients/mpd/libmpdclient.h Mon May 19 00:21:51 2008 -0400 ++@@ -0,0 +1,670 @@ +++/* libmpdclient +++ (c)2003-2006 by Warren Dukes (warren.dukes@gmail.com) +++ This project's homepage is: http://www.musicpd.org +++ +++ Redistribution and use in source and binary forms, with or without +++ modification, are permitted provided that the following conditions +++ are met: +++ +++ - Redistributions of source code must retain the above copyright +++ notice, this list of conditions and the following disclaimer. +++ +++ - Redistributions in binary form must reproduce the above copyright +++ notice, this list of conditions and the following disclaimer in the +++ documentation and/or other materials provided with the distribution. +++ +++ - Neither the name of the Music Player Daemon nor the names of its +++ contributors may be used to endorse or promote products derived from +++ this software without specific prior written permission. +++ +++ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +++ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +++ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +++ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +++ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +++ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +++ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +++ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +++ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +++ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +++ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +++*/ +++ +++#ifndef LIBMPDCLIENT_H +++#define LIBMPDCLIENT_H +++ +++#ifdef WIN32 +++# define __W32API_USE_DLLIMPORT__ 1 +++#endif +++ +++#include +++#include +++#define MPD_BUFFER_MAX_LENGTH 50000 +++#define MPD_ERRORSTR_MAX_LENGTH 1000 +++#define MPD_WELCOME_MESSAGE "OK MPD " +++ +++#define MPD_ERROR_TIMEOUT 10 /* timeout trying to talk to mpd */ +++#define MPD_ERROR_SYSTEM 11 /* system error */ +++#define MPD_ERROR_UNKHOST 12 /* unknown host */ +++#define MPD_ERROR_CONNPORT 13 /* problems connecting to port on host */ +++#define MPD_ERROR_NOTMPD 14 /* mpd not running on port at host */ +++#define MPD_ERROR_NORESPONSE 15 /* no response on attempting to connect */ +++#define MPD_ERROR_SENDING 16 /* error sending command */ +++#define MPD_ERROR_CONNCLOSED 17 /* connection closed by mpd */ +++#define MPD_ERROR_ACK 18 /* ACK returned! */ +++#define MPD_ERROR_BUFFEROVERRUN 19 /* Buffer was overrun! */ +++ +++#define MPD_ACK_ERROR_UNK -1 +++#define MPD_ERROR_AT_UNK -1 +++ +++#define MPD_ACK_ERROR_NOT_LIST 1 +++#define MPD_ACK_ERROR_ARG 2 +++#define MPD_ACK_ERROR_PASSWORD 3 +++#define MPD_ACK_ERROR_PERMISSION 4 +++#define MPD_ACK_ERROR_UNKNOWN_CMD 5 +++ +++#define MPD_ACK_ERROR_NO_EXIST 50 +++#define MPD_ACK_ERROR_PLAYLIST_MAX 51 +++#define MPD_ACK_ERROR_SYSTEM 52 +++#define MPD_ACK_ERROR_PLAYLIST_LOAD 53 +++#define MPD_ACK_ERROR_UPDATE_ALREADY 54 +++#define MPD_ACK_ERROR_PLAYER_SYNC 55 +++#define MPD_ACK_ERROR_EXIST 56 +++ +++#ifdef __cplusplus +++extern "C" { +++#endif +++ +++typedef enum mpd_TagItems +++{ +++ MPD_TAG_ITEM_ARTIST, +++ MPD_TAG_ITEM_ALBUM, +++ MPD_TAG_ITEM_TITLE, +++ MPD_TAG_ITEM_TRACK, +++ MPD_TAG_ITEM_NAME, +++ MPD_TAG_ITEM_GENRE, +++ MPD_TAG_ITEM_DATE, +++ MPD_TAG_ITEM_COMPOSER, +++ MPD_TAG_ITEM_PERFORMER, +++ MPD_TAG_ITEM_COMMENT, +++ MPD_TAG_ITEM_DISC, +++ MPD_TAG_ITEM_FILENAME, +++ MPD_TAG_ITEM_ANY, +++ MPD_TAG_NUM_OF_ITEM_TYPES +++} mpd_TagItems; +++ +++extern char * mpdTagItemKeys[MPD_TAG_NUM_OF_ITEM_TYPES]; +++ +++/* internal stuff don't touch this struct */ +++typedef struct _mpd_ReturnElement { +++ char * name; +++ char * value; +++} mpd_ReturnElement; +++ +++/* mpd_Connection +++ * holds info about connection to mpd +++ * use error, and errorStr to detect errors +++ */ +++typedef struct _mpd_Connection { +++ /* use this to check the version of mpd */ +++ int version[3]; +++ /* IMPORTANT, you want to get the error messages from here */ +++ char errorStr[MPD_ERRORSTR_MAX_LENGTH+1]; +++ int errorCode; +++ int errorAt; +++ /* this will be set to MPD_ERROR_* if there is an error, 0 if not */ +++ int error; +++ /* DON'T TOUCH any of the rest of this stuff */ +++ int sock; +++ char buffer[MPD_BUFFER_MAX_LENGTH+1]; +++ int buflen; +++ int bufstart; +++ int doneProcessing; +++ int listOks; +++ int doneListOk; +++ int commandList; +++ mpd_ReturnElement * returnElement; +++ struct timeval timeout; +++ char *request; +++} mpd_Connection; +++ +++/* mpd_newConnection +++ * use this to open a new connection +++ * you should use mpd_closeConnection, when your done with the connection, +++ * even if an error has occurred +++ * _timeout_ is the connection timeout period in seconds +++ */ +++mpd_Connection * mpd_newConnection(const char * host, int port, float timeout); +++ +++void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout); +++ +++/* mpd_closeConnection +++ * use this to close a connection and free'ing subsequent memory +++ */ +++void mpd_closeConnection(mpd_Connection * connection); +++ +++/* mpd_clearError +++ * clears error +++ */ +++void mpd_clearError(mpd_Connection * connection); +++ +++/* STATUS STUFF */ +++ +++/* use these with status.state to determine what state the player is in */ +++#define MPD_STATUS_STATE_UNKNOWN 0 +++#define MPD_STATUS_STATE_STOP 1 +++#define MPD_STATUS_STATE_PLAY 2 +++#define MPD_STATUS_STATE_PAUSE 3 +++ +++/* us this with status.volume to determine if mpd has volume support */ +++#define MPD_STATUS_NO_VOLUME -1 +++ +++/* mpd_Status +++ * holds info return from status command +++ */ +++typedef struct mpd_Status { +++ /* 0-100, or MPD_STATUS_NO_VOLUME when there is no volume support */ +++ int volume; +++ /* 1 if repeat is on, 0 otherwise */ +++ int repeat; +++ /* 1 if random is on, 0 otherwise */ +++ int random; +++ /* playlist length */ +++ int playlistLength; +++ /* playlist, use this to determine when the playlist has changed */ +++ long long playlist; +++ /* use with MPD_STATUS_STATE_* to determine state of player */ +++ int state; +++ /* crossfade setting in seconds */ +++ int crossfade; +++ /* if a song is currently selected (always the case when state is +++ * PLAY or PAUSE), this is the position of the currently +++ * playing song in the playlist, beginning with 0 +++ */ +++ int song; +++ /* Song ID of the currently selected song */ +++ int songid; +++ /* time in seconds that have elapsed in the currently playing/paused +++ * song +++ */ +++ int elapsedTime; +++ /* length in seconds of the currently playing/paused song */ +++ int totalTime; +++ /* current bit rate in kbs */ +++ int bitRate; +++ /* audio sample rate */ +++ unsigned int sampleRate; +++ /* audio bits */ +++ int bits; +++ /* audio channels */ +++ int channels; +++ /* 1 if mpd is updating, 0 otherwise */ +++ int updatingDb; +++ /* error */ +++ char * error; +++} mpd_Status; +++ +++void mpd_sendStatusCommand(mpd_Connection * connection); +++ +++/* mpd_getStatus +++ * returns status info, be sure to free it with mpd_freeStatus() +++ * call this after mpd_sendStatusCommand() +++ */ +++mpd_Status * mpd_getStatus(mpd_Connection * connection); +++ +++/* mpd_freeStatus +++ * free's status info malloc'd and returned by mpd_getStatus +++ */ +++void mpd_freeStatus(mpd_Status * status); +++ +++typedef struct _mpd_Stats { +++ int numberOfArtists; +++ int numberOfAlbums; +++ int numberOfSongs; +++ unsigned long uptime; +++ unsigned long dbUpdateTime; +++ unsigned long playTime; +++ unsigned long dbPlayTime; +++} mpd_Stats; +++ +++typedef struct _mpd_SearchStats { +++ int numberOfSongs; +++ unsigned long playTime; +++} mpd_SearchStats; +++ +++void mpd_sendStatsCommand(mpd_Connection * connection); +++ +++mpd_Stats * mpd_getStats(mpd_Connection * connection); +++ +++void mpd_freeStats(mpd_Stats * stats); +++ +++mpd_SearchStats * mpd_getSearchStats(mpd_Connection * connection); +++ +++void mpd_freeSearchStats(mpd_SearchStats * stats); +++ +++/* SONG STUFF */ +++ +++#define MPD_SONG_NO_TIME -1 +++#define MPD_SONG_NO_NUM -1 +++#define MPD_SONG_NO_ID -1 +++ +++/* mpd_Song +++ * for storing song info returned by mpd +++ */ +++typedef struct _mpd_Song { +++ /* filename of song */ +++ char * file; +++ /* artist, maybe NULL if there is no tag */ +++ char * artist; +++ /* title, maybe NULL if there is no tag */ +++ char * title; +++ /* album, maybe NULL if there is no tag */ +++ char * album; +++ /* track, maybe NULL if there is no tag */ +++ char * track; +++ /* name, maybe NULL if there is no tag; it's the name of the current +++ * song, f.e. the icyName of the stream */ +++ char * name; +++ /* date */ +++ char *date; +++ +++ /* added by qball */ +++ /* Genre */ +++ char *genre; +++ /* Composer */ +++ char *composer; +++ /* Performer */ +++ char *performer; +++ /* Disc */ +++ char *disc; +++ /* Comment */ +++ char *comment; +++ +++ /* length of song in seconds, check that it is not MPD_SONG_NO_TIME */ +++ int time; +++ /* if plchanges/playlistinfo/playlistid used, is the position of the +++ * song in the playlist */ +++ int pos; +++ /* song id for a song in the playlist */ +++ int id; +++} mpd_Song; +++ +++/* mpd_newSong +++ * use to allocate memory for a new mpd_Song +++ * file, artist, etc all initialized to NULL +++ * if your going to assign values to file, artist, etc +++ * be sure to malloc or strdup the memory +++ * use mpd_freeSong to free the memory for the mpd_Song, it will also +++ * free memory for file, artist, etc, so don't do it yourself +++ */ +++mpd_Song * mpd_newSong(void); +++ +++/* mpd_freeSong +++ * use to free memory allocated by mpd_newSong +++ * also it will free memory pointed to by file, artist, etc, so be careful +++ */ +++void mpd_freeSong(mpd_Song * song); +++ +++/* mpd_songDup +++ * works like strDup, but for a mpd_Song +++ */ +++mpd_Song * mpd_songDup(mpd_Song * song); +++ +++/* DIRECTORY STUFF */ +++ +++/* mpd_Directory +++ * used to store info fro directory (right now that just the path) +++ */ +++typedef struct _mpd_Directory { +++ char * path; +++} mpd_Directory; +++ +++/* mpd_newDirectory +++ * allocates memory for a new directory +++ * use mpd_freeDirectory to free this memory +++ */ +++mpd_Directory * mpd_newDirectory(void); +++ +++/* mpd_freeDirectory +++ * used to free memory allocated with mpd_newDirectory, and it frees +++ * path of mpd_Directory, so be careful +++ */ +++void mpd_freeDirectory(mpd_Directory * directory); +++ +++/* mpd_directoryDup +++ * works like strdup, but for mpd_Directory +++ */ +++mpd_Directory * mpd_directoryDup(mpd_Directory * directory); +++ +++/* PLAYLISTFILE STUFF */ +++ +++/* mpd_PlaylistFile +++ * stores info about playlist file returned by lsinfo +++ */ +++typedef struct _mpd_PlaylistFile { +++ char * path; +++} mpd_PlaylistFile; +++ +++/* mpd_newPlaylistFile +++ * allocates memory for new mpd_PlaylistFile, path is set to NULL +++ * free this memory with mpd_freePlaylistFile +++ */ +++mpd_PlaylistFile * mpd_newPlaylistFile(void); +++ +++/* mpd_freePlaylist +++ * free memory allocated for freePlaylistFile, will also free +++ * path, so be careful +++ */ +++void mpd_freePlaylistFile(mpd_PlaylistFile * playlist); +++ +++/* mpd_playlistFileDup +++ * works like strdup, but for mpd_PlaylistFile +++ */ +++mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist); +++ +++/* INFO ENTITY STUFF */ +++ +++/* the type of entity returned from one of the commands that generates info +++ * use in conjunction with mpd_InfoEntity.type +++ */ +++#define MPD_INFO_ENTITY_TYPE_DIRECTORY 0 +++#define MPD_INFO_ENTITY_TYPE_SONG 1 +++#define MPD_INFO_ENTITY_TYPE_PLAYLISTFILE 2 +++ +++/* mpd_InfoEntity +++ * stores info on stuff returned info commands +++ */ +++typedef struct mpd_InfoEntity { +++ /* the type of entity, use with MPD_INFO_ENTITY_TYPE_* to determine +++ * what this entity is (song, directory, etc...) +++ */ +++ int type; +++ /* the actual data you want, mpd_Song, mpd_Directory, etc */ +++ union { +++ mpd_Directory * directory; +++ mpd_Song * song; +++ mpd_PlaylistFile * playlistFile; +++ } info; +++} mpd_InfoEntity; +++ +++mpd_InfoEntity * mpd_newInfoEntity(void); +++ +++void mpd_freeInfoEntity(mpd_InfoEntity * entity); +++ +++/* INFO COMMANDS AND STUFF */ +++ +++/* use this function to loop over after calling Info/Listall functions */ +++mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection); +++ +++/* fetches the currently seeletect song (the song referenced by status->song +++ * and status->songid*/ +++void mpd_sendCurrentSongCommand(mpd_Connection * connection); +++ +++/* songNum of -1, means to display the whole list */ +++void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songNum); +++ +++/* songId of -1, means to display the whole list */ +++void mpd_sendPlaylistIdCommand(mpd_Connection * connection, int songId); +++ +++/* use this to get the changes in the playlist since version _playlist_ */ +++void mpd_sendPlChangesCommand(mpd_Connection * connection, long long playlist); +++ +++/** +++ * @param connection: A valid and connected mpd_Connection. +++ * @param playlist: The playlist version you want the diff with. +++ * A more bandwidth efficient version of the mpd_sendPlChangesCommand. +++ * It only returns the pos+id of the changes song. +++ */ +++void mpd_sendPlChangesPosIdCommand(mpd_Connection * connection, long long playlist); +++ +++/* recursivel fetches all songs/dir/playlists in "dir* (no metadata is +++ * returned) */ +++void mpd_sendListallCommand(mpd_Connection * connection, const char * dir); +++ +++/* same as sendListallCommand, but also metadata is returned */ +++void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir); +++ +++/* non-recursive version of ListallInfo */ +++void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir); +++ +++#define MPD_TABLE_ARTIST MPD_TAG_ITEM_ARTIST +++#define MPD_TABLE_ALBUM MPD_TAG_ITEM_ALBUM +++#define MPD_TABLE_TITLE MPD_TAG_ITEM_TITLE +++#define MPD_TABLE_FILENAME MPD_TAG_ITEM_FILENAME +++ +++void mpd_sendSearchCommand(mpd_Connection * connection, int table, +++ const char * str); +++ +++void mpd_sendFindCommand(mpd_Connection * connection, int table, +++ const char * str); +++ +++/* LIST TAG COMMANDS */ +++ +++/* use this function fetch next artist entry, be sure to free the returned +++ * string. NULL means there are no more. Best used with sendListArtists +++ */ +++char * mpd_getNextArtist(mpd_Connection * connection); +++ +++char * mpd_getNextAlbum(mpd_Connection * connection); +++ +++char * mpd_getNextTag(mpd_Connection *connection, int type); +++ +++/* list artist or albums by artist, arg1 should be set to the artist if +++ * listing albums by a artist, otherwise NULL for listing all artists or albums +++ */ +++void mpd_sendListCommand(mpd_Connection * connection, int table, +++ const char * arg1); +++ +++/* SIMPLE COMMANDS */ +++ +++void mpd_sendAddCommand(mpd_Connection * connection, const char * file); +++ +++int mpd_sendAddIdCommand(mpd_Connection *connection, const char *file); +++ +++void mpd_sendDeleteCommand(mpd_Connection * connection, int songNum); +++ +++void mpd_sendDeleteIdCommand(mpd_Connection * connection, int songNum); +++ +++void mpd_sendSaveCommand(mpd_Connection * connection, const char * name); +++ +++void mpd_sendLoadCommand(mpd_Connection * connection, const char * name); +++ +++void mpd_sendRmCommand(mpd_Connection * connection, const char * name); +++ +++void mpd_sendRenameCommand(mpd_Connection *connection, const char *from, +++ const char *to); +++ +++void mpd_sendShuffleCommand(mpd_Connection * connection); +++ +++void mpd_sendClearCommand(mpd_Connection * connection); +++ +++/* use this to start playing at the beginning, useful when in random mode */ +++#define MPD_PLAY_AT_BEGINNING -1 +++ +++void mpd_sendPlayCommand(mpd_Connection * connection, int songNum); +++ +++void mpd_sendPlayIdCommand(mpd_Connection * connection, int songNum); +++ +++void mpd_sendStopCommand(mpd_Connection * connection); +++ +++void mpd_sendPauseCommand(mpd_Connection * connection, int pauseMode); +++ +++void mpd_sendNextCommand(mpd_Connection * connection); +++ +++void mpd_sendPrevCommand(mpd_Connection * connection); +++ +++void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to); +++ +++void mpd_sendMoveIdCommand(mpd_Connection * connection, int from, int to); +++ +++void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2); +++ +++void mpd_sendSwapIdCommand(mpd_Connection * connection, int song1, int song2); +++ +++void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time); +++ +++void mpd_sendSeekIdCommand(mpd_Connection * connection, int song, int time); +++ +++void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode); +++ +++void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode); +++ +++void mpd_sendSetvolCommand(mpd_Connection * connection, int volumeChange); +++ +++/* WARNING: don't use volume command, its depreacted */ +++void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange); +++ +++void mpd_sendCrossfadeCommand(mpd_Connection * connection, int seconds); +++ +++void mpd_sendUpdateCommand(mpd_Connection * connection, char * path); +++ +++/* returns the update job id, call this after a update command*/ +++int mpd_getUpdateId(mpd_Connection * connection); +++ +++void mpd_sendPasswordCommand(mpd_Connection * connection, const char * pass); +++ +++/* after executing a command, when your done with it to get its status +++ * (you want to check connection->error for an error) +++ */ +++void mpd_finishCommand(mpd_Connection * connection); +++ +++/* command list stuff, use this to do things like add files very quickly */ +++void mpd_sendCommandListBegin(mpd_Connection * connection); +++ +++void mpd_sendCommandListOkBegin(mpd_Connection * connection); +++ +++void mpd_sendCommandListEnd(mpd_Connection * connection); +++ +++/* advance to the next listOk +++ * returns 0 if advanced to the next list_OK, +++ * returns -1 if it advanced to an OK or ACK */ +++int mpd_nextListOkCommand(mpd_Connection * connection); +++ +++typedef struct _mpd_OutputEntity { +++ int id; +++ char * name; +++ int enabled; +++} mpd_OutputEntity; +++ +++void mpd_sendOutputsCommand(mpd_Connection * connection); +++ +++mpd_OutputEntity * mpd_getNextOutput(mpd_Connection * connection); +++ +++void mpd_sendEnableOutputCommand(mpd_Connection * connection, int outputId); +++ +++void mpd_sendDisableOutputCommand(mpd_Connection * connection, int outputId); +++ +++void mpd_freeOutputElement(mpd_OutputEntity * output); +++ +++/** +++ * @param connection a #mpd_Connection +++ * +++ * Queries mpd for the allowed commands +++ */ +++void mpd_sendCommandsCommand(mpd_Connection * connection); +++ +++/** +++ * @param connection a #mpd_Connection +++ * +++ * Queries mpd for the not allowed commands +++ */ +++void mpd_sendNotCommandsCommand(mpd_Connection * connection); +++ +++/** +++ * @param connection a #mpd_Connection +++ * +++ * returns the next supported command. +++ * +++ * @returns a string, needs to be free'ed +++ */ +++char *mpd_getNextCommand(mpd_Connection *connection); +++ +++void mpd_sendUrlHandlersCommand(mpd_Connection * connection); +++ +++char *mpd_getNextHandler(mpd_Connection * connection); +++ +++void mpd_sendTagTypesCommand(mpd_Connection * connection); +++ +++char *mpd_getNextTagType(mpd_Connection * connection); +++ +++/** +++ * @param connection a MpdConnection +++ * @param path the path to the playlist. +++ * +++ * List the content, with full metadata, of a stored playlist. +++ * +++ */ +++void mpd_sendListPlaylistInfoCommand(mpd_Connection *connection, char *path); +++ +++/** +++ * @param connection a MpdConnection +++ * @param path the path to the playlist. +++ * +++ * List the content of a stored playlist. +++ * +++ */ +++void mpd_sendListPlaylistCommand(mpd_Connection *connection, char *path); +++ +++/** +++ * @param connection a #mpd_Connection +++ * @param exact if to match exact +++ * +++ * starts a search, use mpd_addConstraintSearch to add +++ * a constraint to the search, and mpd_commitSearch to do the actual search +++ */ +++void mpd_startSearch(mpd_Connection *connection, int exact); +++ +++/** +++ * @param connection a #mpd_Connection +++ * @param type +++ * @param name +++ */ +++void mpd_addConstraintSearch(mpd_Connection *connection, int type, const char *name); +++ +++/** +++ * @param connection a #mpd_Connection +++ */ +++void mpd_commitSearch(mpd_Connection *connection); +++ +++/** +++ * @param connection a #mpd_Connection +++ * @param type The type to search for +++ * +++ * starts a search for fields... f.e. get a list of artists would be: +++ * @code +++ * mpd_startFieldSearch(connection, MPD_TAG_ITEM_ARTIST); +++ * mpd_commitSearch(connection); +++ * @endcode +++ * +++ * or get a list of artist in genre "jazz" would be: +++ * @code +++ * mpd_startFieldSearch(connection, MPD_TAG_ITEM_ARTIST); +++ * mpd_addConstraintSearch(connection, MPD_TAG_ITEM_GENRE, "jazz") +++ * mpd_commitSearch(connection); +++ * @endcode +++ * +++ * mpd_startSearch will return a list of songs (and you need mpd_getNextInfoEntity) +++ * this one will return a list of only one field (the one specified with type) and you need +++ * mpd_getNextTag to get the results +++ */ +++void mpd_startFieldSearch(mpd_Connection *connection, int type); +++ +++void mpd_startPlaylistSearch(mpd_Connection *connection, int exact); +++ +++void mpd_startStatsSearch(mpd_Connection *connection); +++ +++void mpd_sendPlaylistClearCommand(mpd_Connection *connection, char *path); +++ +++void mpd_sendPlaylistAddCommand(mpd_Connection *connection, +++ char *playlist, char *path); +++ +++void mpd_sendPlaylistMoveCommand(mpd_Connection *connection, +++ char *playlist, int from, int to); +++ +++void mpd_sendPlaylistDeleteCommand(mpd_Connection *connection, +++ char *playlist, int pos); +++#ifdef __cplusplus +++} +++#endif +++ +++#endif ++diff -r 171db9560cb5 clients/mpd/mpdinterface.cc ++--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++++ b/clients/mpd/mpdinterface.cc Mon May 19 00:21:51 2008 -0400 ++@@ -0,0 +1,404 @@ +++#include "mpdinterface.h" +++#include "immsutil.h" +++#include +++#include +++#include +++ +++namespace mpd_interface +++{ +++ position_err::position_err(int position, int playlist_length) +++ { +++ std::ostringstream s; +++ s << "Attempted to access song of invalid position in playlist. " +++ "Used position: " << position << ", playlist size: " +++ << playlist_length; +++ msg = s.str(); +++ } +++ +++ std::string Song::music_directory = ""; +++ +++ void Song::set_default_dir(std::string path) +++ { +++ music_directory = path; +++ if(!path.empty() && path[path.length()-1]!='/') { +++ music_directory.append("/"); +++ } +++ } +++ +++ bool operator==(const Song& first, const Song& second) +++ { +++ return (first.song_path == second.song_path) && +++ (first.pl_pos == second.pl_pos) && +++ (first.song_length == second.song_length); +++ } +++ bool operator!=(const Song& first, const Song& second) +++ { +++ return !(first == second); +++ } +++ +++ // determines whether the line contains the given parameter in the +++ // mpd.conf fashion +++ bool contains_parameter(const std::string& line, const std::string& parameter) +++ { +++ if(line.empty() || line[0]=='#') return false; +++ +++ std::string::size_type pos = line.find(parameter); +++ if(pos == std::string::npos) return false; +++ +++ while(pos>0) { +++ if(!isspace(line[pos-1])) return false; +++ --pos; +++ } +++ return true; +++ } +++ +++ // returns the value of the given parameter from an mpd.config line +++ std::string get_value(const std::string& line, +++ const std::string& parameter) +++ { +++ if(!contains_parameter(line, parameter)) return ""; +++ +++ std::string::size_type start = line.find_first_of("\""); +++ ++start; // position of the first non-quote char +++ if(start == std::string::npos || start>=line.size()) return ""; +++ +++ std::string::size_type end = line.find_last_of("\""); +++ if(end == std::string::npos) return ""; +++ +++ std::string result = line.substr(start, end-start); +++ return result; +++ } +++ +++ config read_configuration(std::string conf_path) throw(config_err) +++ { +++ static const std::string address_param = "bind_to_address"; +++ static const std::string port_param = "port"; +++ static const std::string dir_param = "music_directory"; +++ +++ if(conf_path == "") +++ { +++ std::string user_path = getenv("HOME"); +++ if(user_path[user_path.size()-1] != '/') user_path.append("/"); +++ user_path.append(".mpdconf"); +++ +++ try { +++ // read the "~/.mpdconf" file +++ return read_configuration(user_path); +++ } +++ catch (config_err) { // "~/.mpdconf" not found +++ // read the "/etc/mpd.conf" instead +++ return read_configuration("/etc/mpd.conf"); +++ } +++ } +++ else +++ { +++ std::ifstream con_f(conf_path.c_str()); +++ if(!con_f) { +++ throw config_err("cannot open the MPD config file \"" + +++ conf_path + "\""); +++ } +++ +++ config result; +++ std::string line; +++ while(getline(con_f, line)) { +++ if(contains_parameter(line, address_param)) { +++ result.hostname = get_value(line, address_param); +++ if(result.hostname.empty()) result.hostname = "localhost"; +++ } +++ else if(contains_parameter(line, port_param)) { +++ std::stringstream s; +++ s << get_value(line, port_param); +++ if(!(s >> result.port)) result.port = 6600; +++ } +++ else if(contains_parameter(line, dir_param)) { +++ result.music_dir = get_value(line, dir_param); +++ } +++ } +++ con_f.close(); +++ if(result.music_dir.empty()) { +++ throw config_err("infalid format of the MPD config file \"" + +++ conf_path + "\""); +++ } +++ +++ return result; +++ } +++ } +++ +++ +++ bool Server::connect(const std::string& hostname, int port) +++ throw(connection_err) +++ { +++ if(connected()) disconnect(); +++ +++ try { +++ connection = mpd_newConnection(hostname.c_str(), port, 10); +++ if(mpd_error()) throw connection_err(connection->errorStr); +++ } +++ catch(connection_err) { +++ disconnect(); +++ throw; +++ } +++ +++ if(connected()) { +++ } +++ return connected(); +++ } +++ +++ void Server::disconnect() +++ { +++ if(connection!=nullptr) { +++ mpd_closeConnection(connection); +++ } +++ connection = nullptr; +++ } +++ +++ bool Server::ack_error() const throw(connection_err) +++ { +++ if(!connected()) throw connection_err(); +++ return connection->error == MPD_ERROR_ACK; +++ } +++ // determines whether the MPD is in error state which is not the ACK error +++ bool Server::mpd_error() const throw(connection_err) +++ { +++ if(!connected()) throw connection_err(); +++ return connection->error!=0 && !ack_error(); +++ } +++ +++ // changes the mpd internal status to the playback_status type +++ playback_status resolve_state(int mpd_state) +++ { +++ switch(mpd_state) +++ { +++ case MPD_STATUS_STATE_PLAY: +++ return playing; +++ case MPD_STATUS_STATE_STOP: +++ return stopped; +++ case MPD_STATUS_STATE_PAUSE: +++ return paused; +++ default: +++ return playing; +++ } +++ } +++ +++ void Server::refresh() throw(connection_err) +++ { +++ if(!connected()) throw connection_err(); +++ try { +++ +++// the mess below is here because otherwise the results in nasty problems +++// when MPD connection times out +++//TODO prolly doesn't have to be everywhere, though. Weed it out +++if(mpd_error()) throw connection_err(connection->errorStr); +++ mpd_sendCommandListOkBegin(connection); +++if(mpd_error()) throw connection_err(connection->errorStr); +++ mpd_sendStatusCommand(connection); +++if(mpd_error()) throw connection_err(connection->errorStr); +++ mpd_sendCurrentSongCommand(connection); +++if(mpd_error()) throw connection_err(connection->errorStr); +++ mpd_sendCommandListEnd(connection); +++if(mpd_error()) throw connection_err(connection->errorStr); +++ +++ mpd_Status * status = mpd_getStatus(connection); +++ if(status!=nullptr) { +++ +++ pl_length = status->playlistLength; +++ pl_version = status->playlist; +++ pb_status = resolve_state(status->state); +++ current_song.set_elapsed(status->elapsedTime); +++ random = status->random; +++ +++ mpd_freeStatus(status); +++ } +++ +++ if(mpd_error()) throw connection_err(connection->errorStr); +++ +++ mpd_nextListOkCommand(connection); +++ mpd_InfoEntity *entity; +++ while((entity = mpd_getNextInfoEntity(connection))) { +++ mpd_Song *song = entity->info.song; +++ +++ if(entity->type!=MPD_INFO_ENTITY_TYPE_SONG) { +++ mpd_freeInfoEntity(entity); +++ continue; +++ } +++ +++ current_song.set_path(song->file); +++ current_song.set_pos(song->pos); +++ current_song.set_length(song->time); +++ mpd_freeInfoEntity(entity); +++ } +++ if(mpd_error()) throw connection_err(connection->errorStr); +++ +++ mpd_finishCommand(connection); +++ if(mpd_error()) throw connection_err(connection->errorStr); +++ } +++ catch(connection_err) { +++ disconnect(); +++ throw; +++ } +++ } +++ +++ Song Server::get_song_info(int pl_pos) +++ throw(connection_err, position_err) +++ { +++ if(!connected()) throw connection_err(); +++ if(pl_pos < 0 || pl_pos >= get_playlist_length()) { +++ throw position_err(pl_pos, get_playlist_length()); +++ } +++ +++ Song result; +++ try { +++ mpd_sendPlaylistInfoCommand(connection, pl_pos); +++ if(mpd_error()) throw connection_err(connection->errorStr); +++ +++ mpd_InfoEntity *entity; +++ while((entity = mpd_getNextInfoEntity(connection))) +++ { +++ mpd_Song *song = entity->info.song; +++ +++ if(entity->type!=MPD_INFO_ENTITY_TYPE_SONG) +++ { +++ mpd_freeInfoEntity(entity); +++ continue; +++ } +++ +++ result = Song(song->file,song->pos,song->time); +++ +++ mpd_freeInfoEntity(entity); +++ } +++ if(mpd_error()) throw connection_err(connection->errorStr); +++ +++ mpd_finishCommand(connection); +++ if(mpd_error()) throw connection_err(connection->errorStr); +++ // shouldn't happen, but just in case +++ if(ack_error()) { +++ refresh(); +++ throw position_err(pl_pos, get_playlist_length()); +++ } +++ } +++ catch(connection_err) +++ { +++ disconnect(); +++ throw; +++ } +++ +++ return result; +++ } +++ +++ void Server::play_song(int pl_pos) throw(connection_err, position_err) +++ { +++ if(!connected()) throw connection_err(); +++ if(pl_pos < 0 || pl_pos >= pl_length) { +++ throw position_err(pl_pos, get_playlist_length()); +++ } +++ +++ try { +++ mpd_sendPlayCommand(connection, pl_pos); +++ mpd_finishCommand(connection); +++ if(mpd_error()) throw connection_err(connection->errorStr); +++ if(ack_error()) throw position_err(pl_pos, get_playlist_length()); +++ } +++ catch(connection_err) { +++ disconnect(); +++ throw; +++ } +++ } +++ // end of SERVER section +++ +++ +++ // Player class +++ Player::Player(): previous_status(stopped), current_status(stopped), +++ previous_song_pos(Song::invalid_pos), current_song_pos(Song::invalid_pos), +++ null_song() +++ { +++ } +++ +++ bool Player::connect() throw(config_err, connection_err) +++ { +++ config conf = read_configuration(); +++ Song::set_default_dir(conf.music_dir); +++ return mpd.connect(conf.hostname, conf.port); +++ } +++ +++ void Player::disconnect() +++ { +++ mpd.disconnect(); +++ } +++ +++ void Player::refresh() +++ { +++ mpd.refresh(); +++ +++ playlist__changed = pl_ver != mpd.get_playlist_version(); +++ if(playlist__changed) { +++ pl_ver = mpd.get_playlist_version(); +++ +++ // update the internal playlist +++ playlist.clear(); +++ playlist.reserve(mpd.get_playlist_length()); +++ for(int i = 0; i +++#include +++ +++namespace mpd_interface +++{ +++const int nullptr = 0; +++ +++/* list of thrown exceptions */ +++ +++ // generic exception that can be thrown by MPD +++ class mpd_err +++ { +++ protected: +++ std::string msg; +++ public: +++ mpd_err(const std::string& message = "unknown error"): msg(message) { } +++ std::string message() const { return msg; } +++ }; +++ +++ // thrown when for whatever reason the connection cannot be maintained +++ // the Player is always set to the disconnected state when this is thrown +++ class connection_err: public mpd_err +++ { +++ public: +++ connection_err(const std::string& message = "not connected"): +++ mpd_err(message) { } +++ }; +++ +++ class config_err: public mpd_err +++ { +++ public: +++ config_err(const std::string& message = "") : mpd_err(message) { } +++ }; +++ +++ // thrown when trying to access element out of the playlist range +++ // contains information about the length of the playlist +++ // and the invalid position +++ class position_err: public mpd_err +++ { +++ int invalid_position; +++ int pl_length; +++ public: +++ position_err(int position, int playlist_length); +++ int pos() const { return invalid_position; } +++ int playlist_length() const {return pl_length; } +++ }; +++/* end of the exception list */ +++ +++ +++ // information about the configuration of MPD +++ // it's the relevant info read from the mpd.config file +++ struct config +++ { +++ std::string hostname; +++ int port; +++ std::string music_dir; +++ }; +++ +++ // attempts to read the configuration file +++ // if no path is specified following actions are taken: +++ // first, the ~/.mpdconf file is looked for +++ // then, the /etc/mpd.conf file is tried. +++ config read_configuration(std::string conf_path="") throw(config_err); +++ +++ +++ // Represents one song in playlist. +++ // Note that when one song is twice in a playlist (their positions differ) +++ // they are two different songs from this class' point of view. +++ class Song +++ { +++ static std::string music_directory; // directory in which the songs are +++ +++ std::string song_path; +++ int pl_pos; +++ int song_length; // time - in seconds +++ int song_elapsed; // time - in seconds +++ +++ public: +++ static const int invalid_pos = MPD_SONG_NO_NUM; +++ static const int invalid_time = MPD_SONG_NO_TIME; +++ +++ static void set_default_dir(std::string path); // set the music directory +++ +++ Song(std::string path = "", int position = invalid_pos, +++ int length = invalid_time, int elapsed = invalid_time): +++ song_path(music_directory+path), pl_pos(position), +++ song_length(length), song_elapsed(elapsed) { } +++ +++ friend bool operator==(const Song& first, const Song& second); +++ friend bool operator!=(const Song& first, const Song& second); +++ +++ std::string path() const { return song_path; } +++ void set_path(std::string path) { song_path = music_directory+path; } +++ int pos() const { return pl_pos; } +++ void set_pos(int position) { pl_pos = position; } +++ int length() const { return song_length; } +++ void set_length(int length) { song_length = length; } +++ int elapsed() const {return song_elapsed; } +++ void set_elapsed(int elapsed) { song_elapsed = elapsed; } +++ }; +++ +++ +++ +++ // the status of the playback - whether it's stopped, paused +++ // or is playing a song right now +++ enum playback_status {playing, stopped, paused} ; +++ +++ // represents the communication with the MPD server +++ // It's a confortable wrapper around the libmpdclient library +++ // When its refresh() method is called it gets all information from server +++ // and stores them so as not to flood the MPD with commands +++ class Server +++ { +++ public: +++ Server(): connection(nullptr) {}; +++ ~Server() { disconnect(); } +++ +++ bool connect(const std::string& hostname="localhost", int port = 6600) +++ throw(connection_err); +++ void disconnect(); +++ bool connected() const {return connection!=nullptr; } +++ +++ +++ void refresh() throw(connection_err); // the Server asks MPD for new values +++ int get_playlist_length() const { return pl_length; } +++ long long get_playlist_version() { return pl_version; } +++ playback_status get_playback_status() { return pb_status; } +++ bool get_random() const {return random; } +++ +++ Song get_song_info(int pl_pos) throw(connection_err,position_err); +++ Song get_current_song() const { return current_song; } +++ +++ // plays the song at the specified position +++ void play_song(int pl_pos) throw(connection_err, position_err); +++ +++ private: +++ Server& operator=(const Server&); +++ Server(const Server&); +++ +++ mpd_Connection *connection; +++ int pl_length; +++ long long pl_version; +++ playback_status pb_status; +++ Song current_song; +++ bool random; +++ +++ +++ // determines whether the actual MPD error state is the ACK error +++ bool ack_error() const throw(connection_err); +++ +++ // determines whether the MPD is in error state which is not the ACK error +++ bool mpd_error() const throw(connection_err); +++ }; +++ +++ +++ +++ enum recent {previous, current}; +++ +++ class Player +++ { +++ public: +++ +++ Player(); +++ +++ bool connect() throw(config_err, connection_err); +++ void disconnect(); +++ bool connected() const { return mpd.connected(); } +++ +++ void refresh(); +++ +++ bool song_changed() {return song__changed; } +++ bool playlist_changed() { return playlist__changed; } +++ bool status_changed() { return status__changed; } +++ +++ // gets the current or the previously played song +++ // song(current) returns currently played song +++ // song(previous) returns the previously played song +++ const Song& song(recent) const; +++ const Song& song(int position) const throw(position_err); +++ int playlist_length() const { return mpd.get_playlist_length(); } +++ bool radnom() const { return mpd.get_random(); } +++ void play_song(int pl_pos); +++ +++ // gets the current or the previous playback status +++ playback_status status(recent) const; +++ +++ +++ private: +++ Player& operator=(const Player&); +++ Player(const Player&); +++ +++ Server mpd; +++ long long pl_ver; +++ playback_status previous_status, current_status; +++ int previous_song_pos, current_song_pos; +++ const Song null_song; +++ std::vector playlist; +++ +++ bool song__changed, playlist__changed, status__changed; +++ }; +++ +++} +++ +++#endif +++ ++diff -r 171db9560cb5 clients/mpd/rules.mk ++--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++++ b/clients/mpd/rules.mk Mon May 19 00:21:51 2008 -0400 ++@@ -0,0 +1,16 @@ +++MDPCPPFLAGS= $(GLIB2CPPFLAGS) +++MPDLDFLAGS= $(GLIB2LDFLAGS) +++MPDCOMMON= mpdinterface.o libmpdclient.o clientstubbase.o libimmscore.a libmodel.a +++ +++ +++immsmpd: immsmpd.o $(MPDCOMMON) +++immsmpd: $(call objects,../clients/mpd) +++immsmpd-CPPFLAGS=$(MDPCPPFLAGS) +++immsmpd-LIBS=$(MPDLDFLAGS) +++ +++ +++MPDDESTDIR=/usr/bin +++ +++immsmpd_install: immsmpd +++ ${INSTALL} -D $^ $(MPDDESTDIR) +++ ++diff -r 171db9560cb5 configure.ac ++--- a/configure.ac Mon May 19 00:16:56 2008 -0400 +++++ b/configure.ac Mon May 19 00:21:51 2008 -0400 ++@@ -260,6 +260,13 @@ ++ saved_libs="$LIBS" ++ ++ PLUGINS="" +++ +++AC_CHECK_PROG(with_mdp, mpd --help, "yes", "no") +++if test "$with_mpd" != "no"; then +++ AC_APPEND(PLUGINS, immsmpd) +++fi +++ +++ ++ AC_CHECK_PROG(with_xmms, xmms-config, "yes", "no") ++ if test "$with_xmms" != "no"; then ++ CPPFLAGS=`xmms-config --cflags` ++diff -r 171db9560cb5 vars.mk.in ++--- a/vars.mk.in Mon May 19 00:16:56 2008 -0400 +++++ b/vars.mk.in Mon May 19 00:21:51 2008 -0400 ++@@ -10,7 +10,7 @@ ++ bindir = @bindir@ ++ datadir = @datadir@ ++ ++-VPATH = ../immscore:../analyzer:../model:../autotag:../immsremote:../utils:../clients:../immsd:../data:../clients/xmms:../clients/bmp:../clients/audacious +++VPATH = ../immscore:../analyzer:../model:../autotag:../immsremote:../utils:../clients:../immsd:../data:../clients/xmms:../clients/bmp:../clients/audacious:../clients/mpd ++ ARFLAGS = rs ++ ++ SHELL = bash +diff -r fb5042644071 debian/patches/04mpd-client-config-init.dpatch +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/debian/patches/04mpd-client-config-init.dpatch Mon May 19 01:12:49 2008 -0400 +@@ -0,0 +1,20 @@ ++#! /bin/sh /usr/share/dpatch/dpatch-run ++## mpd-client-config-init.dpatch by Fabien Niñoles ++## ++## All lines beginning with `## DP:' are a description of the patch. ++## DP: Initialize default configuration. ++ ++@DPATCH@ ++ ++diff -r 019508c19157 clients/mpd/mpdinterface.cc ++--- a/clients/mpd/mpdinterface.cc Mon May 19 00:21:51 2008 -0400 +++++ b/clients/mpd/mpdinterface.cc Mon May 19 00:51:06 2008 -0400 ++@@ -98,7 +98,7 @@ ++ conf_path + "\""); ++ } ++ ++- config result; +++ config result = { "localhost", 6600, "" }; ++ std::string line; ++ while(getline(con_f, line)) { ++ if(contains_parameter(line, address_param)) { diff -r 000000000000 -r a17355c8ffbd mpd-client-debian --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mpd-client-debian Mon May 19 01:29:48 2008 -0400 @@ -0,0 +1,83 @@ +diff -r 895b2cad584f debian/changelog +--- a/debian/changelog Mon May 19 01:12:49 2008 -0400 ++++ b/debian/changelog Mon May 19 01:27:29 2008 -0400 +@@ -1,3 +1,10 @@ ++imms (3.1.0~rc4-3.0.1) unstable; urgency=low ++ ++ * Add mpd to imms clients from ++ http://www.fi.muni.cz/~xsedov/index.php?page=projects. ++ ++ -- Fabien Ninoles Mon, 19 May 2008 01:19:22 -0400 ++ + imms (3.1.0~rc4-3) unstable; urgency=low + + * Really add 02compile-with-gcc-4.3 (Closes: #476824) +diff -r 895b2cad584f debian/control +--- a/debian/control Mon May 19 01:12:49 2008 -0400 ++++ b/debian/control Mon May 19 01:27:29 2008 -0400 +@@ -4,7 +4,7 @@ + Maintainer: Artur R. Czechowski + Standards-Version: 3.7.3 + Homepage: http://www.luminal.org/wiki/index.php/IMMS +-Build-Depends: debhelper (>> 5.0.0), audacious-plugins-dev, libsqlite3-dev (>=3.2.2), libpcre3-dev (>=4.3), libtag1-dev, libvorbis-dev (>=1.0), libfftw3-dev, libglib2.0-dev, zlib1g-dev, libxss-dev, libtorch3-dev, libglade2-dev, dpatch, autoconf, automake ++Build-Depends: debhelper (>> 5.0.0), audacious-plugins-dev, libsqlite3-dev (>=3.2.2), libpcre3-dev (>=4.3), libtag1-dev, libvorbis-dev (>=1.0), libfftw3-dev, libglib2.0-dev, zlib1g-dev, libxss-dev, libtorch3-dev, libglade2-dev, dpatch, autoconf, automake, libmpd-dev + + Package: imms-common + Architecture: any +@@ -39,3 +39,25 @@ + ratings even if they are moved and/or their tags change. + . + This package contains audacious plugin ++ ++Package: imms-mpd ++Architecture: any ++Depends: ${shlibs:Depends}, ${misc:Depends}, imms-common (= ${binary:Version}) ++Provides: imms-plugin ++Description: Unobtrusive, automatic, and learning mpd playlist manager ++ IMMS is an intelligent playlist plug-in that tracks your ++ listening patterns and dynamically adapts to your taste. ++ . ++ Major features include: ++ . ++ - Rating and playlist adjustment are done completely transparently to ++ the user. IMMS is super easy to use! ++ - Though IMMS will mostly play "good" songs, occasionally less popular ++ ones are given a chance to earn your favor. ++ - IMMS does a better job of shuffling than player ++ It is able to recognize different versions of the same song (eg. remixes) ++ and not play them too often. ++ - IMMS uses smart file identification that allows files to keep their ++ ratings even if they are moved and/or their tags change. ++ . ++ This package contains the mpd client daemon. +diff -r 895b2cad584f debian/copyright +--- a/debian/copyright Mon May 19 01:12:49 2008 -0400 ++++ b/debian/copyright Mon May 19 01:27:29 2008 -0400 +@@ -4,6 +4,9 @@ + The current maintainer is Artur R. Czechowski + + It was downloaded from http://www.luminal.org/phpwiki/index.php/IMMS ++with immsmpd download from ++http://www.fi.muni.cz/~xsedov/index.php?page=projects by ++xsedov@fi.muni.cz + + Author: Michael Grigoriev + +diff -r 895b2cad584f debian/imms-mpd.dirs +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/debian/imms-mpd.dirs Mon May 19 01:27:29 2008 -0400 +@@ -0,0 +1,2 @@ ++usr/bin ++usr/share/doc +diff -r 895b2cad584f debian/rules +--- a/debian/rules Mon May 19 01:12:49 2008 -0400 ++++ b/debian/rules Mon May 19 01:27:29 2008 -0400 +@@ -67,6 +67,8 @@ + $(MAKE) programs_install prefix=$(CURDIR)/debian/imms-common/usr + install -m 0644 -D build/libaudaciousimms.so $(CURDIR)/debian/imms-audacious`pkg-config --variable=general_plugin_dir audacious`/libaudaciousimms.so + ln -sf imms-common debian/imms-audacious/usr/share/doc/imms-audacious ++ install -m 0644 -D build/immsmpd $(CURDIR)/debian/imms-mpd/usr/bin/immsmpd ++ ln -sf imms-common debian/imms-mpd/usr/share/doc/imms-mpd + + # Build architecture-independent files here. + binary-indep: build install diff -r 000000000000 -r a17355c8ffbd series --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/series Mon May 19 01:29:48 2008 -0400 @@ -0,0 +1,2 @@ +mpd-client +mpd-client-debian