1496 lines
52 KiB
C
1496 lines
52 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
#include "empdd.h"
|
|
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <Ecore_Con.h>
|
|
#include <Eldbus.h>
|
|
#include <mpd/client.h>
|
|
#include <mpd/parser.h>
|
|
#include <mpd/async.h>
|
|
|
|
#define STRING_SAFETY(X) X ?: ""
|
|
#define S(X) #X
|
|
|
|
#define RECONNECT_THRESH 5
|
|
|
|
typedef struct E_Slist E_Slist;
|
|
struct E_Slist
|
|
{
|
|
E_Slist *next;
|
|
unsigned int command;
|
|
void *data;
|
|
};
|
|
|
|
typedef struct EMPD
|
|
{
|
|
Ecore_Con_Server *svr;
|
|
Ecore_Fd_Handler *fdh;
|
|
Ecore_Poller *pinger;
|
|
struct mpd_settings *settings;
|
|
struct mpd_async *async;
|
|
struct mpd_parser *parser;
|
|
|
|
|
|
Eina_List *pending;
|
|
Eina_List *current_queue;
|
|
void *cur;
|
|
E_Slist *cmds, *last;
|
|
|
|
unsigned int reconnect_fail;
|
|
Eina_Bool connected;
|
|
Eina_Bool idle : 1;
|
|
} EMPD;
|
|
|
|
typedef enum
|
|
{
|
|
EMPD_COMMAND_NONE = 0,
|
|
EMPD_COMMAND_PASSWORD,
|
|
EMPD_COMMAND_STATUS,
|
|
EMPD_COMMAND_CONFIG,
|
|
EMPD_COMMAND_CURRENT_SONG,
|
|
EMPD_COMMAND_PLAY,
|
|
EMPD_COMMAND_PAUSE,
|
|
EMPD_COMMAND_TOGGLE_PAUSE,
|
|
EMPD_COMMAND_STOP,
|
|
EMPD_COMMAND_NEXT,
|
|
EMPD_COMMAND_PREVIOUS,
|
|
EMPD_COMMAND_SEEK,
|
|
EMPD_COMMAND_PLAY_ID,
|
|
EMPD_COMMAND_QUEUE_LIST,
|
|
EMPD_COMMAND_CLEAR_LIST,
|
|
EMPD_COMMAND_DELETE_LIST,
|
|
EMPD_COMMAND_ADD_LIST,
|
|
EMPD_COMMAND_ADDID_LIST,
|
|
EMPD_COMMAND_MOVE_LIST,
|
|
EMPD_COMMAND_SAVE_PLAYLIST,
|
|
EMPD_COMMAND_ADDTO_PLAYLIST,
|
|
EMPD_COMMAND_LOAD_PLAYLIST,
|
|
EMPD_COMMAND_REPEAT,
|
|
EMPD_COMMAND_RANDOM,
|
|
EMPD_COMMAND_SINGLE,
|
|
EMPD_COMMAND_CONSUME,
|
|
EMPD_COMMAND_LSINFO,
|
|
EMPD_COMMAND_UPDATE,
|
|
EMPD_COMMAND_FIND,
|
|
EMPD_COMMAND_IDLE,
|
|
EMPD_COMMAND_QUEUE_CHANGES_META,
|
|
} EMPD_Command;
|
|
|
|
static const char *cmd_txt[] =
|
|
{
|
|
#define CMD_TXT(X) \
|
|
[X] = S(X),
|
|
CMD_TXT(EMPD_COMMAND_NONE)
|
|
CMD_TXT(EMPD_COMMAND_PASSWORD)
|
|
CMD_TXT(EMPD_COMMAND_STATUS)
|
|
CMD_TXT(EMPD_COMMAND_CONFIG)
|
|
CMD_TXT(EMPD_COMMAND_CURRENT_SONG)
|
|
CMD_TXT(EMPD_COMMAND_PLAY)
|
|
CMD_TXT(EMPD_COMMAND_PAUSE)
|
|
CMD_TXT(EMPD_COMMAND_TOGGLE_PAUSE)
|
|
CMD_TXT(EMPD_COMMAND_STOP)
|
|
CMD_TXT(EMPD_COMMAND_NEXT)
|
|
CMD_TXT(EMPD_COMMAND_PREVIOUS)
|
|
CMD_TXT(EMPD_COMMAND_SEEK)
|
|
CMD_TXT(EMPD_COMMAND_PLAY_ID)
|
|
CMD_TXT(EMPD_COMMAND_QUEUE_LIST)
|
|
CMD_TXT(EMPD_COMMAND_CLEAR_LIST)
|
|
CMD_TXT(EMPD_COMMAND_DELETE_LIST)
|
|
CMD_TXT(EMPD_COMMAND_ADD_LIST)
|
|
CMD_TXT(EMPD_COMMAND_ADDID_LIST)
|
|
CMD_TXT(EMPD_COMMAND_MOVE_LIST)
|
|
CMD_TXT(EMPD_COMMAND_SAVE_PLAYLIST)
|
|
CMD_TXT(EMPD_COMMAND_ADDTO_PLAYLIST)
|
|
CMD_TXT(EMPD_COMMAND_LOAD_PLAYLIST)
|
|
CMD_TXT(EMPD_COMMAND_REPEAT)
|
|
CMD_TXT(EMPD_COMMAND_RANDOM)
|
|
CMD_TXT(EMPD_COMMAND_SINGLE)
|
|
CMD_TXT(EMPD_COMMAND_CONSUME)
|
|
CMD_TXT(EMPD_COMMAND_LSINFO)
|
|
CMD_TXT(EMPD_COMMAND_UPDATE)
|
|
CMD_TXT(EMPD_COMMAND_FIND)
|
|
CMD_TXT(EMPD_COMMAND_IDLE)
|
|
CMD_TXT(EMPD_COMMAND_QUEUE_CHANGES_META)
|
|
};
|
|
|
|
typedef enum
|
|
{
|
|
EMPD_SIGNAL_LOGIN_FAIL,
|
|
EMPD_SIGNAL_PERMISSION_FAIL,
|
|
EMPD_SIGNAL_CONNECTED,
|
|
EMPD_SIGNAL_DISCONNECTED,
|
|
EMPD_SIGNAL_STATUS,
|
|
EMPD_SIGNAL_CURRENT_SONG,
|
|
EMPD_SIGNAL_QUEUE_LIST,
|
|
EMPD_SIGNAL_QUEUE_CHANGES_META,
|
|
EMPD_SIGNAL_DATABASE_UPDATE_BEGIN,
|
|
EMPD_SIGNAL_DATABASE_UPDATE_END,
|
|
} EMPD_Signals;
|
|
|
|
static const Eldbus_Signal empd_signals[] =
|
|
{
|
|
[EMPD_SIGNAL_LOGIN_FAIL] = {"LoginFailed", ELDBUS_ARGS({"s", "host"}, {"u", "port"}), 0},
|
|
[EMPD_SIGNAL_PERMISSION_FAIL] = {"PermissionFailed", ELDBUS_ARGS({"s", "host"}, {"u", "port"}, {"u", "code"}), 0},
|
|
[EMPD_SIGNAL_CONNECTED] = {"Connected", ELDBUS_ARGS({"s", "host"}, {"u", "port"}), 0},
|
|
[EMPD_SIGNAL_DISCONNECTED] = {"Disconnected", NULL, 0},
|
|
[EMPD_SIGNAL_STATUS] = {"Status", ELDBUS_ARGS({"i", "volume"}, {"b", "repeat"}, {"b", "random"},
|
|
{"b", "single"}, {"b", "consume"}, {"u", "queue_version"}, {"u", "queue_length"},
|
|
{"d", "mixrampdb"}, {"u", "state"}, {"i", "song_pos"}, {"i", "songid"},
|
|
{"u", "song_length"}, {"u", "song_elapsed"}, {"u", "bitrate"},
|
|
{"u", "sample_rate"}, {"u", "sample_bits"}, {"u", "sample_channels"}, {"i", "next_song_pos"}, {"i", "next_songid"},
|
|
{"u", "update_id"}), 0},
|
|
[EMPD_SIGNAL_CURRENT_SONG] = {"CurrentSong", ELDBUS_ARGS({"s", "uri"}, {"t", "last_modified"}, {"u", "duration"},
|
|
{"s", "artist"}, {"s", "composer"}, {"s", "title"}, {"s", "album"}, {"i", "track"},
|
|
{"s", "name"}, {"s", "date"}, {"s", "disc"}, {"s", "genre"}, {"i", "song_pos"}, {"i", "songid"}), 0},
|
|
[EMPD_SIGNAL_QUEUE_LIST] = {"QueueList", ELDBUS_ARGS({"a(stusssisssii)", "array_of_songs"}), 0},
|
|
[EMPD_SIGNAL_QUEUE_CHANGES_META] = {"QueueChangesMeta", ELDBUS_ARGS({"a(stusssisssii)", "array_of_songs"}), 0},
|
|
[EMPD_SIGNAL_DATABASE_UPDATE_BEGIN] = {"DatabaseUpdateBegin", NULL, 0},
|
|
[EMPD_SIGNAL_DATABASE_UPDATE_END] = {"DatabaseUpdateEnd", NULL, 0},
|
|
{NULL, NULL, 0}
|
|
};
|
|
|
|
static double elapsed = 0;
|
|
|
|
static EMPD *empd = NULL;
|
|
static Eina_List *handlers = NULL;
|
|
static Eina_Mempool *slist_mempool = NULL;
|
|
|
|
static Eldbus_Connection *dbus_conn = NULL;
|
|
static Eldbus_Service_Interface *empd_iface = NULL;
|
|
|
|
static Eina_Bool fetching_queue = EINA_FALSE;
|
|
static unsigned int empd_queue_version = 0;
|
|
static unsigned int empd_queue_length = 0;
|
|
static int empd_songid = -1;
|
|
static int empd_dbupdate = 0;
|
|
|
|
static inline void
|
|
fdh_update(void)
|
|
{
|
|
int flags;
|
|
|
|
flags = mpd_async_events(empd->async);
|
|
flags &= ~MPD_ASYNC_EVENT_ERROR & ~MPD_ASYNC_EVENT_HUP;
|
|
ecore_main_fd_handler_active_set(empd->fdh, flags);
|
|
}
|
|
|
|
static inline E_Slist *
|
|
cmd_append(EMPD_Command cmd)
|
|
{
|
|
E_Slist *es;
|
|
|
|
es = eina_mempool_malloc(slist_mempool, sizeof(E_Slist));
|
|
es->command = cmd;
|
|
es->next = NULL;
|
|
es->data = NULL;
|
|
if (empd->cmds)
|
|
empd->last->next = es;
|
|
else
|
|
empd->cmds = es;
|
|
if (empd->idle && (cmd != EMPD_COMMAND_IDLE))
|
|
{
|
|
empd->idle = 0;
|
|
mpd_async_send_command(empd->async, "noidle", NULL);
|
|
fdh_update();
|
|
}
|
|
//fprintf(stderr, "CMD ADD: %s\n", cmd_txt[es->command]);
|
|
empd->last = es;
|
|
//es = empd->cmds;
|
|
//while (es)
|
|
//{
|
|
//fprintf(stderr, "CMD ADD CYCLE: %s\n", cmd_txt[es->command]);
|
|
//es = es->next;
|
|
//}
|
|
return es;
|
|
}
|
|
|
|
static inline void
|
|
cmd_remove(void)
|
|
{
|
|
E_Slist *es = empd->cmds;
|
|
|
|
if (!es) return;
|
|
//fprintf(stderr, "CMD REMOVE: %s\n", cmd_txt[es->command]);
|
|
//if (es->next)
|
|
//fprintf(stderr, "CMD NEXT: %s\n", cmd_txt[es->next->command]);
|
|
//else
|
|
//fprintf(stderr, "CMD END\n");
|
|
empd->cmds = es->next;
|
|
if (es->command == EMPD_COMMAND_QUEUE_LIST)
|
|
fetching_queue = EINA_FALSE;
|
|
eina_mempool_free(slist_mempool, es);
|
|
if (!empd->cmds)
|
|
empd->last = NULL;
|
|
empd->cur = NULL;
|
|
if (!empd->connected) return;
|
|
if (!empd->cmds)
|
|
{
|
|
mpd_async_send_command(empd->async, "idle database playlist player update options", NULL, NULL);
|
|
empd->idle = 1;
|
|
ecore_main_fd_handler_active_set(empd->fdh, ECORE_FD_WRITE);
|
|
cmd_append(EMPD_COMMAND_IDLE);
|
|
}
|
|
//es = empd->cmds;
|
|
//while (es)
|
|
//{
|
|
//fprintf(stderr, "CMD REMOVE CYCLE: %s\n", cmd_txt[es->command]);
|
|
//if (es == es->next) abort();
|
|
//es = es->next;
|
|
//}
|
|
}
|
|
|
|
static void
|
|
cmd_clear(void)
|
|
{
|
|
while (empd->cmds)
|
|
cmd_remove();
|
|
}
|
|
|
|
static inline EMPD_Command
|
|
cmd_get(void)
|
|
{
|
|
return empd->cmds ? empd->cmds->command : EMPD_COMMAND_NONE;
|
|
}
|
|
|
|
static inline Ecore_Con_Type
|
|
conn_type_get(const char *host)
|
|
{
|
|
if (host[0] == '/') return ECORE_CON_LOCAL_SYSTEM;
|
|
return ECORE_CON_REMOTE_TCP;
|
|
}
|
|
|
|
static void
|
|
conn_reset(void)
|
|
{
|
|
if (empd->connected)
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DISCONNECTED);
|
|
empd_queue_version = 0;
|
|
empd_songid = -1;
|
|
empd_dbupdate = 0;
|
|
empd->connected = 0;
|
|
ecore_con_server_del(empd->svr);
|
|
empd->svr = NULL;
|
|
if (empd->async)
|
|
mpd_async_free(empd->async);
|
|
empd->async = NULL;
|
|
ecore_main_fd_handler_del(empd->fdh);
|
|
empd->fdh = NULL;
|
|
ecore_poller_del(empd->pinger);
|
|
empd->pinger = NULL;
|
|
cmd_clear();
|
|
}
|
|
|
|
static void
|
|
reconnect(const char *host, int port, const char *pass)
|
|
{
|
|
struct mpd_settings *s = empd->settings;
|
|
const char *h = host;
|
|
int p = port;
|
|
|
|
if (pass && (!pass[0]))
|
|
pass = NULL;
|
|
|
|
if (s)
|
|
h = host && host[0] ? host : mpd_settings_get_host(s);
|
|
if (h && (h[0] == '/'))
|
|
p = -1;
|
|
else if (s)
|
|
p = port == -1 ? (int)mpd_settings_get_port(s) : port;
|
|
if (host || s)
|
|
s = mpd_settings_new(h, p, 3000, NULL, pass);
|
|
if ((empd->async || empd->svr) && empd->settings &&
|
|
((!host) || (!host[0]) || (!strcmp(host, mpd_settings_get_host(empd->settings)))) &&
|
|
((port == -1) || ((unsigned int)port == mpd_settings_get_port(empd->settings))))
|
|
{
|
|
if (empd->async)
|
|
{
|
|
if ((!empd->connected) && ((!pass) || (!pass[0])))
|
|
{
|
|
/* attempting to reconnect to current server with no password
|
|
* despite needing a password: paddlin.
|
|
*/
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_LOGIN_FAIL,
|
|
mpd_settings_get_host(s),
|
|
mpd_settings_get_port(s));
|
|
}
|
|
else
|
|
{
|
|
cmd_append(EMPD_COMMAND_PASSWORD);
|
|
mpd_async_send_command(empd->async, "password", mpd_settings_get_password(s), NULL);
|
|
fdh_update();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
conn_reset();
|
|
h = mpd_settings_get_host(s);
|
|
empd->svr = ecore_con_server_connect(conn_type_get(h), h, p, empd);
|
|
}
|
|
if (empd->settings)
|
|
mpd_settings_free(empd->settings);
|
|
empd->settings = s;
|
|
}
|
|
|
|
static Eina_Bool
|
|
pinger_cb(void *d EINA_UNUSED)
|
|
{
|
|
cmd_append(EMPD_COMMAND_STATUS);
|
|
mpd_async_send_command(empd->async, "status", NULL);
|
|
fdh_update();
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static void
|
|
queue_list_info_send(Eldbus_Message *msg, Eina_List *queue_list, unsigned int start, long num, Eina_Bool sig)
|
|
{
|
|
Eldbus_Message_Iter *iter, *array, *struc;
|
|
Eina_List *l;
|
|
struct mpd_song *so;
|
|
unsigned int cur = 0;
|
|
|
|
iter = eldbus_message_iter_get(msg);
|
|
array = eldbus_message_iter_container_new(iter, 'a', "(stusssisssii)");
|
|
EINA_LIST_FOREACH(queue_list, l, so)
|
|
{
|
|
/* holy shit. */
|
|
const char *track = mpd_song_get_tag(so, MPD_TAG_TRACK, 0);
|
|
|
|
if (cur++ < start) continue;
|
|
eldbus_message_iter_arguments_append(array, "(stusssisssii)", &struc);
|
|
eldbus_message_iter_arguments_append(struc, "stusssisssii",
|
|
STRING_SAFETY(mpd_song_get_uri(so)), mpd_song_get_last_modified(so), mpd_song_get_duration(so),
|
|
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ARTIST, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_TITLE, 0)),
|
|
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ALBUM, 0)),
|
|
track ? atoi(track) : 0, STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_NAME, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DATE, 0)),
|
|
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DISC, 0)), mpd_song_get_pos(so), mpd_song_get_id(so));
|
|
eldbus_message_iter_container_close(array, struc);
|
|
if (mpd_song_get_pos(so) == num) break;
|
|
}
|
|
eldbus_message_iter_container_close(iter, array);
|
|
if (sig)
|
|
eldbus_service_signal_send(empd_iface, msg);
|
|
}
|
|
|
|
static void
|
|
queue_list_send(Eldbus_Message *msg, unsigned int start, long num)
|
|
{
|
|
Eina_Bool sig = !msg;
|
|
|
|
if (!msg)
|
|
msg = eldbus_service_signal_new(empd_iface, EMPD_SIGNAL_QUEUE_LIST);
|
|
queue_list_info_send(msg, empd->current_queue, start, num, sig);
|
|
}
|
|
|
|
static Eina_Bool
|
|
queue_fetch(void)
|
|
{
|
|
if (fetching_queue) return EINA_FALSE;
|
|
fetching_queue = EINA_TRUE;
|
|
cmd_append(EMPD_COMMAND_QUEUE_LIST);
|
|
mpd_async_send_command(empd->async, "playlistinfo", NULL);
|
|
fdh_update();
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
fdh_func(void *d EINA_UNUSED, Ecore_Fd_Handler *fdh EINA_UNUSED)
|
|
{
|
|
char *txt;
|
|
Eina_Bool status_pending = EINA_FALSE;
|
|
|
|
if (!mpd_async_io(empd->async, MPD_ASYNC_EVENT_READ | MPD_ASYNC_EVENT_WRITE))
|
|
{
|
|
Eina_Bool connected = empd->connected;
|
|
sleep(2);
|
|
conn_reset();
|
|
if (connected && (empd->reconnect_fail < RECONNECT_THRESH))
|
|
{
|
|
reconnect(NULL, -1, mpd_settings_get_password(empd->settings));
|
|
empd->reconnect_fail++;
|
|
}
|
|
return EINA_FALSE;
|
|
}
|
|
fdh_update();
|
|
txt = mpd_async_recv_line(empd->async);
|
|
if (!txt) return EINA_TRUE;
|
|
while (txt)
|
|
{
|
|
const char *name = NULL, *value = NULL;
|
|
enum mpd_parser_result res = mpd_parser_feed(empd->parser, txt);
|
|
switch (res)
|
|
{
|
|
case MPD_PARSER_MALFORMED: abort(); //FIXME
|
|
/**
|
|
* MPD has returned "ACK" with an error code. Call
|
|
* mpd_parser_get_server_error() to get the error code.
|
|
*/
|
|
case MPD_PARSER_ERROR:
|
|
if (mpd_parser_get_server_error(empd->parser) == MPD_SERVER_ERROR_PASSWORD)
|
|
{
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_LOGIN_FAIL,
|
|
mpd_settings_get_host(empd->settings),
|
|
mpd_settings_get_port(empd->settings));
|
|
empd->connected = 0;
|
|
goto end;
|
|
}
|
|
if (mpd_parser_get_server_error(empd->parser) == MPD_SERVER_ERROR_PERMISSION)
|
|
{
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_PERMISSION_FAIL,
|
|
mpd_settings_get_host(empd->settings),
|
|
mpd_settings_get_port(empd->settings),
|
|
mpd_parser_get_at(empd->parser));
|
|
}
|
|
if (mpd_async_get_error(empd->async) == MPD_ERROR_CLOSED)
|
|
{
|
|
sleep(2);
|
|
conn_reset();
|
|
return EINA_FALSE;
|
|
}
|
|
break;
|
|
/**
|
|
* MPD has returned a name-value pair. Call
|
|
* mpd_parser_get_name() and mpd_parser_get_value().
|
|
*/
|
|
case MPD_PARSER_PAIR:
|
|
/**
|
|
* MPD has returned "OK" or "list_OK" (check with
|
|
* mpd_parser_is_discrete()).
|
|
*/
|
|
name = mpd_parser_get_name(empd->parser);
|
|
value = mpd_parser_get_value(empd->parser);
|
|
//fprintf(stderr, "[%s]|%s: %s\n", cmd_txt[cmd_get()], name, value);
|
|
break;
|
|
case MPD_PARSER_SUCCESS:
|
|
//fprintf(stderr, "[%s]|SUCCESS\n", cmd_txt[cmd_get()]);
|
|
if (cmd_get() == EMPD_COMMAND_PASSWORD)
|
|
pinger_cb(NULL);
|
|
break;
|
|
}
|
|
switch (cmd_get())
|
|
{
|
|
case EMPD_COMMAND_IDLE:
|
|
{
|
|
if (!name) break; //ending idle
|
|
switch (mpd_idle_name_parse(value))
|
|
{
|
|
case MPD_IDLE_DATABASE:
|
|
if (empd_dbupdate)
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DATABASE_UPDATE_END);
|
|
empd_dbupdate = 0;
|
|
break;
|
|
case MPD_IDLE_QUEUE:
|
|
if (!empd->pinger)
|
|
pinger_cb(NULL);
|
|
break;
|
|
case MPD_IDLE_UPDATE:
|
|
if (!empd_dbupdate)
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DATABASE_UPDATE_BEGIN);
|
|
empd_dbupdate = -1;
|
|
case MPD_IDLE_PLAYER:
|
|
case MPD_IDLE_OPTIONS:
|
|
if (status_pending) break;
|
|
pinger_cb(NULL);
|
|
status_pending = EINA_TRUE;
|
|
break;
|
|
default: break;
|
|
}
|
|
break;
|
|
}
|
|
case EMPD_COMMAND_STATUS:
|
|
{
|
|
struct mpd_status *st;
|
|
|
|
if (!empd->cur)
|
|
empd->cur = mpd_status_begin();
|
|
st = empd->cur;
|
|
|
|
//fprintf(stderr, "[%s]|HIT\n", cmd_txt[cmd_get()]);
|
|
|
|
if (res == MPD_PARSER_PAIR)
|
|
{
|
|
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
|
|
mpd_status_feed(st, &(struct mpd_pair){name, value});
|
|
}
|
|
else if (res == MPD_PARSER_SUCCESS)
|
|
{
|
|
const struct mpd_audio_format *af = mpd_status_get_audio_format(st);
|
|
int update;
|
|
|
|
if (!empd->connected)
|
|
{
|
|
empd->connected = 1;
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_CONNECTED,
|
|
mpd_settings_get_host(empd->settings),
|
|
mpd_settings_get_port(empd->settings));
|
|
}
|
|
//fprintf(stderr, "[%s]|SENT\n", cmd_txt[cmd_get()]);
|
|
if (empd_songid != mpd_status_get_song_id(st))
|
|
{
|
|
cmd_append(EMPD_COMMAND_CURRENT_SONG);
|
|
mpd_async_send_command(empd->async, "currentsong", NULL);
|
|
}
|
|
empd_songid = mpd_status_get_song_id(st);
|
|
update = mpd_status_get_update_id(st);
|
|
if (empd_dbupdate && (!update))
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DATABASE_UPDATE_END);
|
|
else if ((!empd_dbupdate) && update)
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DATABASE_UPDATE_BEGIN);
|
|
empd_dbupdate = update;
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_STATUS,
|
|
mpd_status_get_volume(st), mpd_status_get_repeat(st), mpd_status_get_random(st),
|
|
mpd_status_get_single(st), mpd_status_get_consume(st), mpd_status_get_queue_version(st), mpd_status_get_queue_length(st),
|
|
mpd_status_get_mixrampdb(st), mpd_status_get_state(st), mpd_status_get_song_pos(st), mpd_status_get_song_id(st),
|
|
mpd_status_get_total_time(st), mpd_status_get_elapsed_time(st), mpd_status_get_kbit_rate(st),
|
|
af ? af->sample_rate : 0, af ? af->bits : 0, af ? af->channels : 0,
|
|
mpd_status_get_next_song_pos(st), mpd_status_get_next_song_id(st), empd_dbupdate);
|
|
empd_queue_length = mpd_status_get_queue_length(st);
|
|
/* if (no queue exists) */
|
|
if ((!empd->current_queue) && (!empd_queue_length))
|
|
empd_queue_version = mpd_status_get_queue_version(st);
|
|
/* if (queue exists but has not been fetched) */
|
|
else if ((!empd->current_queue) && (mpd_status_get_queue_length(st)))
|
|
{
|
|
queue_fetch();
|
|
empd_queue_version = mpd_status_get_queue_version(st);
|
|
}
|
|
else
|
|
{
|
|
unsigned int version = mpd_status_get_queue_version(st);
|
|
for (; empd_queue_version < version; empd_queue_version++)
|
|
{
|
|
char buf[64];
|
|
|
|
snprintf(buf, sizeof(buf), "%u", empd_queue_version);
|
|
cmd_append(EMPD_COMMAND_QUEUE_CHANGES_META);
|
|
mpd_async_send_command(empd->async, "plchanges", buf, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
}
|
|
status_pending = EINA_FALSE;
|
|
switch (mpd_status_get_state(st))
|
|
{
|
|
case MPD_STATE_PLAY:
|
|
if (!empd->pinger)
|
|
empd->pinger = ecore_poller_add(ECORE_POLLER_CORE, 8, pinger_cb, NULL);
|
|
break;
|
|
default:
|
|
E_FREE_FUNC(empd->pinger, ecore_poller_del);
|
|
break;
|
|
}
|
|
|
|
mpd_status_free(st);
|
|
}
|
|
}
|
|
break;
|
|
case EMPD_COMMAND_CONFIG:
|
|
{
|
|
if (res == MPD_PARSER_PAIR)
|
|
{
|
|
Eldbus_Message *msg;
|
|
|
|
msg = empd->cmds->data;
|
|
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
|
|
eldbus_message_arguments_append(msg, "s", value);
|
|
eldbus_connection_send(dbus_conn, msg, NULL, NULL, -1);
|
|
}
|
|
else if (res == MPD_PARSER_ERROR)
|
|
{
|
|
Eldbus_Message *msg;
|
|
|
|
msg = empd->cmds->data;
|
|
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
|
|
eldbus_message_arguments_append(msg, "s", "");
|
|
eldbus_connection_send(dbus_conn, msg, NULL, NULL, -1);
|
|
}
|
|
}
|
|
case EMPD_COMMAND_CURRENT_SONG:
|
|
case EMPD_COMMAND_QUEUE_LIST:
|
|
case EMPD_COMMAND_QUEUE_CHANGES_META:
|
|
{
|
|
struct mpd_song *so;
|
|
|
|
so = empd->cur;
|
|
//fprintf(stderr, "[%s]|HIT: %d\n", cmd_txt[cmd_get()], res);
|
|
|
|
if (res == MPD_PARSER_PAIR)
|
|
{
|
|
if (!empd->cur)
|
|
{
|
|
empd->cur = mpd_song_begin(&(struct mpd_pair){name, value});
|
|
break;
|
|
}
|
|
mpd_song_feed(so, &(struct mpd_pair){name, value});
|
|
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
|
|
if (cmd_get() != EMPD_COMMAND_CURRENT_SONG)
|
|
{
|
|
if (!strcmp(name, "Id"))
|
|
{
|
|
/* at end of current song, push to list
|
|
* for sending all at once later
|
|
*/
|
|
empd->pending = eina_list_append(empd->pending, so);
|
|
empd->cur = NULL;
|
|
}
|
|
}
|
|
}
|
|
else if (cmd_get() == EMPD_COMMAND_CURRENT_SONG)
|
|
{
|
|
const char *track;
|
|
|
|
if (!so) break; //no current song
|
|
track = mpd_song_get_tag(so, MPD_TAG_TRACK, 0);
|
|
|
|
//fprintf(stderr, "CURRENT\n");
|
|
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_CURRENT_SONG,
|
|
STRING_SAFETY(mpd_song_get_uri(so)), mpd_song_get_last_modified(so), mpd_song_get_duration(so),
|
|
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ARTIST, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_COMPOSER, 0)),
|
|
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_TITLE, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ALBUM, 0)),
|
|
track ? atoi(track) : 0, STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_NAME, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DATE, 0)),
|
|
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DISC, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_GENRE, 0)),
|
|
mpd_song_get_pos(so), mpd_song_get_id(so));
|
|
mpd_song_free(so);
|
|
}
|
|
else if (cmd_get() == EMPD_COMMAND_QUEUE_CHANGES_META)
|
|
{
|
|
Eina_List *l, *ll;
|
|
Eldbus_Message *msg;
|
|
|
|
if (res != MPD_PARSER_SUCCESS)
|
|
break;
|
|
|
|
msg = eldbus_service_signal_new(empd_iface, EMPD_SIGNAL_QUEUE_CHANGES_META);
|
|
queue_list_info_send(msg, empd->pending, 0, -1, 1);
|
|
if (!empd->pending)
|
|
{
|
|
while (eina_list_count(empd->current_queue) > empd_queue_length)
|
|
{
|
|
mpd_song_free(eina_list_last_data_get(empd->current_queue));
|
|
empd->current_queue = eina_list_remove_list(empd->current_queue,
|
|
eina_list_last(empd->current_queue));
|
|
}
|
|
break;
|
|
}
|
|
so = eina_list_data_get(empd->pending);
|
|
EINA_LIST_FOREACH_SAFE(eina_list_nth_list(empd->current_queue, mpd_song_get_pos(so)), l, ll, so)
|
|
{
|
|
mpd_song_free(so);
|
|
empd->current_queue = eina_list_remove_list(empd->current_queue, l);
|
|
}
|
|
empd->current_queue = eina_list_merge(empd->current_queue, empd->pending);
|
|
empd->pending = NULL;
|
|
}
|
|
else if (cmd_get() == EMPD_COMMAND_QUEUE_LIST)
|
|
{
|
|
if (res != MPD_PARSER_SUCCESS)
|
|
break;
|
|
E_FREE_LIST(empd->current_queue, mpd_song_free);
|
|
empd->current_queue = empd->pending;
|
|
empd->pending = NULL;
|
|
if (empd->current_queue)
|
|
queue_list_send(NULL, 0, -1);
|
|
}
|
|
}
|
|
break;
|
|
case EMPD_COMMAND_LSINFO:
|
|
case EMPD_COMMAND_FIND:
|
|
{
|
|
//fprintf(stderr, "[%s]|HIT\n", cmd_txt[cmd_get()]);
|
|
|
|
if (res == MPD_PARSER_PAIR)
|
|
{
|
|
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
|
|
if ((!empd->cur) || (!mpd_entity_feed(empd->cur, &(struct mpd_pair){name, value})))
|
|
{
|
|
empd->cur = mpd_entity_begin(&(struct mpd_pair){name, value});
|
|
empd->pending = eina_list_append(empd->pending, empd->cur);
|
|
}
|
|
}
|
|
else if (res == MPD_PARSER_ERROR)
|
|
{
|
|
Eldbus_Message *msg;
|
|
Eldbus_Message_Iter *iter, *array;
|
|
Eldbus_Message_Iter *struc;
|
|
|
|
msg = empd->cmds->data;
|
|
iter = eldbus_message_iter_get(msg);
|
|
array = eldbus_message_iter_container_new(iter, 'a', "(iv)");
|
|
eldbus_message_iter_arguments_append(array, "(iv)", &struc);
|
|
eldbus_message_iter_container_close(array, struc);
|
|
eldbus_message_iter_container_close(iter, array);
|
|
eldbus_connection_send(dbus_conn, msg, NULL, NULL, -1);
|
|
}
|
|
else
|
|
{
|
|
struct mpd_entity *ent;
|
|
Eldbus_Message *msg;
|
|
Eldbus_Message_Iter *iter, *array;
|
|
|
|
//fprintf(stderr, "[%s]|"EINA_COLOR_BLUE"SEND"EINA_COLOR_RESET" %g\n", cmd_txt[cmd_get()], ecore_time_get() - elapsed);
|
|
elapsed = 0;
|
|
|
|
msg = empd->cmds->data;
|
|
iter = eldbus_message_iter_get(msg);
|
|
array = eldbus_message_iter_container_new(iter, 'a', "(iv)");
|
|
EINA_LIST_FREE(empd->pending, ent)
|
|
{
|
|
const struct mpd_song *so;
|
|
const struct mpd_playlist *pl;
|
|
const struct mpd_directory *dir;
|
|
const char *track;
|
|
Eldbus_Message_Iter *struc, *variant, *s2;
|
|
|
|
if (mpd_entity_get_type(ent) == MPD_ENTITY_TYPE_UNKNOWN) continue;
|
|
|
|
eldbus_message_iter_arguments_append(array, "(iv)", &struc);
|
|
eldbus_message_iter_basic_append(struc, 'i', mpd_entity_get_type(ent));
|
|
switch (mpd_entity_get_type(ent))
|
|
{
|
|
case MPD_ENTITY_TYPE_SONG:
|
|
so = mpd_entity_get_song(ent);
|
|
track = mpd_song_get_tag(so, MPD_TAG_TRACK, 0);
|
|
variant = eldbus_message_iter_container_new(struc, 'v', "(stusssisss)");
|
|
eldbus_message_iter_arguments_append(variant, "(stusssisss)", &s2);
|
|
eldbus_message_iter_arguments_append(s2, "stusssisss",
|
|
STRING_SAFETY(mpd_song_get_uri(so)), mpd_song_get_last_modified(so), mpd_song_get_duration(so),
|
|
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ARTIST, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_TITLE, 0)),
|
|
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ALBUM, 0)),
|
|
track ? atoi(track) : 0, STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_NAME, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DATE, 0)),
|
|
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DISC, 0)));
|
|
break;
|
|
case MPD_ENTITY_TYPE_PLAYLIST:
|
|
pl = mpd_entity_get_playlist(ent);
|
|
variant = eldbus_message_iter_container_new(struc, 'v', "(st)");
|
|
eldbus_message_iter_arguments_append(variant, "(st)", &s2);
|
|
eldbus_message_iter_arguments_append(s2, "st",
|
|
mpd_playlist_get_path(pl), mpd_playlist_get_last_modified(pl));
|
|
break;
|
|
case MPD_ENTITY_TYPE_DIRECTORY:
|
|
dir = mpd_entity_get_directory(ent);
|
|
variant = eldbus_message_iter_container_new(struc, 'v', "(st)");
|
|
eldbus_message_iter_arguments_append(variant, "(st)", &s2);
|
|
eldbus_message_iter_arguments_append(s2, "st",
|
|
mpd_directory_get_path(dir), mpd_directory_get_last_modified(dir));
|
|
break;
|
|
default:
|
|
mpd_entity_free(ent);
|
|
continue;
|
|
}
|
|
eldbus_message_iter_container_close(variant, s2);
|
|
eldbus_message_iter_container_close(struc, variant);
|
|
eldbus_message_iter_container_close(array, struc);
|
|
mpd_entity_free(ent);
|
|
}
|
|
eldbus_message_iter_container_close(iter, array);
|
|
eldbus_connection_send(dbus_conn, msg, NULL, NULL, -1);
|
|
empd->cur = NULL;
|
|
}
|
|
}
|
|
break;
|
|
default: break;
|
|
}
|
|
end:
|
|
if ((res == MPD_PARSER_SUCCESS) || (res == MPD_PARSER_ERROR))
|
|
cmd_remove();
|
|
txt = mpd_async_recv_line(empd->async);
|
|
//fprintf(stderr, "[%s]|NEXT LINE\n", cmd_txt[cmd_get()]);
|
|
}
|
|
fdh_update();
|
|
//fprintf(stderr, "[%s]|FDH_DONE\n", cmd_txt[cmd_get()]);
|
|
return EINA_TRUE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
del(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Con_Event_Server_Del *ev EINA_UNUSED)
|
|
{
|
|
if (!empd->async)
|
|
{
|
|
empd->svr = NULL;
|
|
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_LOGIN_FAIL,
|
|
mpd_settings_get_host(empd->settings),
|
|
mpd_settings_get_port(empd->settings));
|
|
}
|
|
return ECORE_CALLBACK_DONE;
|
|
}
|
|
|
|
static Eina_Bool
|
|
data_cb(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Con_Event_Server_Data *ev)
|
|
{
|
|
int fd, flags;
|
|
char *txt;
|
|
|
|
empd->reconnect_fail = 0;
|
|
fd = dup(ecore_con_server_fd_get(ev->server));
|
|
fcntl(fd, F_SETFL, O_NONBLOCK);
|
|
|
|
flags = fcntl(fd, F_GETFD);
|
|
flags |= FD_CLOEXEC;
|
|
fcntl(fd, F_SETFD, flags);
|
|
empd->fdh = ecore_main_fd_handler_add(fd, ECORE_FD_READ | ECORE_FD_ERROR, fdh_func, NULL, NULL, NULL);
|
|
empd->async = mpd_async_new(fd);
|
|
ecore_con_server_del(ev->server);
|
|
empd->svr = NULL;
|
|
|
|
/* this will always just be the first line
|
|
* OK MPD 0.18.0\n
|
|
*/
|
|
txt = ev->data;
|
|
txt[ev->size - 1] = 0;
|
|
if (!empd->parser)
|
|
empd->parser = mpd_parser_new();
|
|
if (mpd_settings_get_password(empd->settings))
|
|
{
|
|
cmd_append(EMPD_COMMAND_PASSWORD);
|
|
mpd_async_send_command(empd->async, "password", mpd_settings_get_password(empd->settings), NULL);
|
|
fdh_update();
|
|
}
|
|
else
|
|
{
|
|
pinger_cb(NULL);
|
|
}
|
|
return ECORE_CALLBACK_DONE;
|
|
}
|
|
|
|
static void
|
|
_dbus_request_name_cb(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
|
|
{
|
|
unsigned int flag;
|
|
|
|
if (eldbus_message_error_get(msg, NULL, NULL))
|
|
fprintf(stderr, "Could not request bus name\n");
|
|
else if (!eldbus_message_arguments_get(msg, "u", &flag))
|
|
fprintf(stderr, "Could not get arguments on on_name_request\n");
|
|
else if (!(flag & ELDBUS_NAME_REQUEST_REPLY_PRIMARY_OWNER))
|
|
fprintf(stderr, "Name already in use\n");
|
|
}
|
|
|
|
#define DBUS_BASIC_CB(name, CMD) \
|
|
static Eldbus_Message * \
|
|
_dbus_##name##_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) \
|
|
{ \
|
|
if (!empd->connected) \
|
|
return eldbus_message_method_return_new(msg); \
|
|
cmd_append(EMPD_COMMAND_##CMD); \
|
|
mpd_async_send_command(empd->async, S(name), NULL); \
|
|
pinger_cb(NULL); \
|
|
return eldbus_message_method_return_new(msg); \
|
|
}
|
|
|
|
DBUS_BASIC_CB(status, STATUS)
|
|
DBUS_BASIC_CB(currentsong, CURRENT_SONG)
|
|
|
|
static Eldbus_Message *
|
|
_dbus_pause_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
Eina_Bool mode;
|
|
const char *sig;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
sig = eldbus_message_signature_get(msg);
|
|
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
|
|
{
|
|
cmd_append(EMPD_COMMAND_PAUSE);
|
|
mpd_async_send_command(empd->async, "pause", mode ? "1" : "0", NULL);
|
|
}
|
|
else
|
|
{
|
|
cmd_append(EMPD_COMMAND_TOGGLE_PAUSE);
|
|
mpd_async_send_command(empd->async, "pause", NULL);
|
|
}
|
|
pinger_cb(NULL);
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_repeat_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
Eina_Bool mode;
|
|
const char *sig;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
sig = eldbus_message_signature_get(msg);
|
|
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
|
|
{
|
|
cmd_append(EMPD_COMMAND_REPEAT);
|
|
mpd_async_send_command(empd->async, "repeat", mode ? "1" : "0", NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_shuffle_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
Eina_Bool mode;
|
|
const char *sig;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
sig = eldbus_message_signature_get(msg);
|
|
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
|
|
{
|
|
cmd_append(EMPD_COMMAND_RANDOM);
|
|
mpd_async_send_command(empd->async, "random", mode ? "1" : "0", NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_single_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
Eina_Bool mode;
|
|
const char *sig;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
sig = eldbus_message_signature_get(msg);
|
|
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
|
|
{
|
|
cmd_append(EMPD_COMMAND_SINGLE);
|
|
mpd_async_send_command(empd->async, "single", mode ? "1" : "0", NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_consume_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
Eina_Bool mode;
|
|
const char *sig;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
sig = eldbus_message_signature_get(msg);
|
|
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
|
|
{
|
|
cmd_append(EMPD_COMMAND_CONSUME);
|
|
mpd_async_send_command(empd->async, "consume", mode ? "1" : "0", NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
DBUS_BASIC_CB(stop, STOP)
|
|
DBUS_BASIC_CB(next, NEXT)
|
|
DBUS_BASIC_CB(previous, PREVIOUS)
|
|
|
|
static Eldbus_Message *
|
|
_dbus_seek_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
unsigned int songid, seconds;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (eldbus_message_arguments_get(msg, "uu", &songid, &seconds))
|
|
{
|
|
char id[256], pos[256];
|
|
|
|
snprintf(id, sizeof(id), "%u", songid);
|
|
snprintf(pos, sizeof(pos), "%u", seconds);
|
|
cmd_append(EMPD_COMMAND_SEEK);
|
|
mpd_async_send_command(empd->async, "seekid", id, pos, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_playid_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
unsigned int songid;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (eldbus_message_arguments_get(msg, "u", &songid))
|
|
{
|
|
char id[256];
|
|
|
|
snprintf(id, sizeof(id), "%u", songid);
|
|
cmd_append(EMPD_COMMAND_PLAY_ID);
|
|
mpd_async_send_command(empd->async, "playid", id, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_play_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *sig;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
sig = eldbus_message_signature_get(msg);
|
|
if (sig && (!strcmp(sig, "u")))
|
|
{
|
|
unsigned int songpos;
|
|
|
|
if (eldbus_message_arguments_get(msg, "u", &songpos))
|
|
{
|
|
char pos[256];
|
|
|
|
snprintf(pos, sizeof(pos), "%u", songpos);
|
|
cmd_append(EMPD_COMMAND_PLAY);
|
|
mpd_async_send_command(empd->async, "play", pos, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cmd_append(EMPD_COMMAND_PLAY);
|
|
mpd_async_send_command(empd->async, "play", NULL, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_queue_list_cached_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
int start, end;
|
|
const char *sig;
|
|
Eldbus_Message *reply;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (!empd->current_queue)
|
|
return eldbus_message_method_return_new(msg);
|
|
|
|
sig = eldbus_message_signature_get(msg);
|
|
if (sig && (!strcmp(sig, "ii")) && eldbus_message_arguments_get(msg, "ii", &start, &end))
|
|
{
|
|
if ((end != -1) && (end < 1))
|
|
return eldbus_message_method_return_new(msg);
|
|
reply = eldbus_message_method_return_new(msg);
|
|
queue_list_send(reply, start, start + end);
|
|
}
|
|
else
|
|
{
|
|
reply = eldbus_message_method_return_new(msg);
|
|
queue_list_send(reply, 0, -1);
|
|
}
|
|
return reply;
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_queue_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
int start, end;
|
|
const char *sig;
|
|
|
|
if (fetching_queue || (!empd->connected)) return eldbus_message_method_return_new(msg);
|
|
sig = eldbus_message_signature_get(msg);
|
|
if (sig && (!strcmp(sig, "ii")) && eldbus_message_arguments_get(msg, "ii", &start, &end))
|
|
{
|
|
char s[256];
|
|
|
|
if ((end != -1) && (end < 1))
|
|
return eldbus_message_method_return_new(msg);
|
|
if (end == -1)
|
|
snprintf(s, sizeof(s), "%d:", start);
|
|
else
|
|
snprintf(s, sizeof(s), "%d:%d", start, end);
|
|
cmd_append(EMPD_COMMAND_QUEUE_LIST);
|
|
mpd_async_send_command(empd->async, "playlistinfo", s, NULL);
|
|
}
|
|
else
|
|
queue_fetch();
|
|
fdh_update();
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
DBUS_BASIC_CB(clear, CLEAR_LIST)
|
|
|
|
static Eldbus_Message *
|
|
_dbus_delete_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
int start, num;
|
|
const char *sig;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
sig = eldbus_message_signature_get(msg);
|
|
if (sig && (!strcmp(sig, "ii")) && eldbus_message_arguments_get(msg, "ii", &start, &num))
|
|
{
|
|
char s[256];
|
|
|
|
if ((num != -1) && (num < 1)) //trying to delete less than 1 song...
|
|
return eldbus_message_method_return_new(msg);
|
|
if (num == -1)
|
|
snprintf(s, sizeof(s), "%d:", start);
|
|
else
|
|
snprintf(s, sizeof(s), "%d:%d", start, start + num);
|
|
cmd_append(EMPD_COMMAND_DELETE_LIST);
|
|
mpd_async_send_command(empd->async, "delete", s, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_add_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *uri;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (eldbus_message_arguments_get(msg, "s", &uri))
|
|
{
|
|
cmd_append(EMPD_COMMAND_ADD_LIST);
|
|
mpd_async_send_command(empd->async, "add", uri, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_addid_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *uri;
|
|
unsigned int pos;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (eldbus_message_arguments_get(msg, "su", &uri, &pos))
|
|
{
|
|
char buf[64];
|
|
|
|
cmd_append(EMPD_COMMAND_ADDID_LIST);
|
|
snprintf(buf, sizeof(buf), "%u", pos);
|
|
mpd_async_send_command(empd->async, "addid", uri, buf, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_move_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
unsigned int start, target;
|
|
int num;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (eldbus_message_arguments_get(msg, "uiu", &start, &num, &target))
|
|
{
|
|
char s[256], s2[256];
|
|
|
|
if ((num != -1) && (num < 1)) //trying to move less than 1 song...
|
|
return eldbus_message_method_return_new(msg);
|
|
if (num == -1)
|
|
snprintf(s, sizeof(s), "%u:", start);
|
|
else
|
|
snprintf(s, sizeof(s), "%u:%d", start, start + num);
|
|
snprintf(s2, sizeof(s2), "%u", target);
|
|
cmd_append(EMPD_COMMAND_MOVE_LIST);
|
|
mpd_async_send_command(empd->async, "move", s, s2, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_save_playlist_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *uri;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (eldbus_message_arguments_get(msg, "s", &uri))
|
|
{
|
|
cmd_append(EMPD_COMMAND_SAVE_PLAYLIST);
|
|
mpd_async_send_command(empd->async, "save", uri, NULL);
|
|
fdh_update();
|
|
}
|
|
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_addto_playlist_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *playlist, *uri;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (eldbus_message_arguments_get(msg, "ss", &playlist, &uri))
|
|
{
|
|
cmd_append(EMPD_COMMAND_ADDTO_PLAYLIST);
|
|
mpd_async_send_command(empd->async, "playlistadd", playlist, uri, NULL);
|
|
fdh_update();
|
|
}
|
|
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_load_playlist_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *uri;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (eldbus_message_arguments_get(msg, "s", &uri))
|
|
{
|
|
cmd_append(EMPD_COMMAND_LOAD_PLAYLIST);
|
|
mpd_async_send_command(empd->async, "load", uri, NULL);
|
|
pinger_cb(NULL);
|
|
}
|
|
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_listinfo_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *path;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (!eldbus_message_arguments_get(msg, "s", &path))
|
|
return eldbus_message_method_return_new(msg);
|
|
cmd_append(EMPD_COMMAND_LSINFO)->data = eldbus_message_method_return_new(msg);
|
|
mpd_async_send_command(empd->async, "lsinfo", path, NULL);
|
|
fdh_update();
|
|
elapsed = ecore_time_get();
|
|
return NULL;
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_update_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *path;
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
if (!eldbus_message_arguments_get(msg, "s", &path))
|
|
return eldbus_message_method_return_new(msg);
|
|
cmd_append(EMPD_COMMAND_UPDATE);
|
|
mpd_async_send_command(empd->async, "update", path, NULL);
|
|
fdh_update();
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_isconnected_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
Eldbus_Message *ret;
|
|
const char *host = "";
|
|
unsigned int port = DEFAULT_PORT;
|
|
|
|
ret = eldbus_message_method_return_new(msg);
|
|
if (empd->settings)
|
|
{
|
|
host = mpd_settings_get_host(empd->settings);
|
|
port = mpd_settings_get_port(empd->settings);
|
|
}
|
|
eldbus_message_arguments_append(ret, "bsu", empd->connected, host, port);
|
|
return ret;
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_connect_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *host = NULL, *pass = NULL;
|
|
int port = DEFAULT_PORT;
|
|
|
|
if (!eldbus_message_arguments_get(msg, "sis", &host, &port, &pass))
|
|
return eldbus_message_method_return_new(msg);
|
|
|
|
if (empd->settings)
|
|
reconnect(host, port, pass);
|
|
else
|
|
{
|
|
empd->settings = mpd_settings_new(host, port, 3000, NULL, pass);
|
|
reconnect(NULL, -1, NULL);
|
|
}
|
|
return eldbus_message_method_return_new(msg);
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_config_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
cmd_append(EMPD_COMMAND_CONFIG)->data = eldbus_message_method_return_new(msg);
|
|
mpd_async_send_command(empd->async, "config", NULL);
|
|
fdh_update();
|
|
return NULL;
|
|
}
|
|
|
|
static Eldbus_Message *
|
|
_dbus_find_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
|
{
|
|
const char *param;
|
|
Empd_Search_Type type;
|
|
const char *args[] =
|
|
{
|
|
"file",
|
|
"any",
|
|
"base",
|
|
"modified-since",
|
|
};
|
|
char buf[4096];
|
|
|
|
if (!empd->connected)
|
|
return eldbus_message_method_return_new(msg);
|
|
|
|
if (!eldbus_message_arguments_get(msg, "us", &type, ¶m))
|
|
return eldbus_message_method_return_new(msg);
|
|
switch (type)
|
|
{
|
|
case EMPD_SEARCH_TYPE_MODIFIED:
|
|
break;
|
|
default: //FIXME
|
|
return eldbus_message_method_return_new(msg);
|
|
break;
|
|
}
|
|
cmd_append(EMPD_COMMAND_FIND)->data = eldbus_message_method_return_new(msg);
|
|
snprintf(buf, sizeof(buf), "find %s", args[type]);
|
|
mpd_async_send_command(empd->async, buf, param, NULL);
|
|
fdh_update();
|
|
return NULL;
|
|
}
|
|
|
|
static const Eldbus_Method empd_methods[] =
|
|
{
|
|
{ "Connect", ELDBUS_ARGS({"s", "host"}, {"i", "port"}, {"s", "password"}), NULL, _dbus_connect_cb, 0},
|
|
{ "IsConnected", NULL, ELDBUS_ARGS({"b", "isconnected"}, {"s", "host"}, {"u", "port"}), _dbus_isconnected_cb, 0},
|
|
{ "Status", NULL, NULL, _dbus_status_cb, 0},
|
|
{ "Config", NULL, ELDBUS_ARGS({"s", "music_directory"}), _dbus_config_cb, 0},
|
|
{ "CurrentSong", NULL, NULL, _dbus_currentsong_cb, 0},
|
|
{ "Play", NULL, NULL, _dbus_play_cb, 0},
|
|
{ "Pause", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_pause_cb, 0},
|
|
{ "TogglePause", NULL, NULL, _dbus_pause_cb, 0},
|
|
{ "Stop", NULL, NULL, _dbus_stop_cb, 0},
|
|
{ "Next", NULL, NULL, _dbus_next_cb, 0},
|
|
{ "Previous", NULL, NULL, _dbus_previous_cb, 0},
|
|
{ "Seek", ELDBUS_ARGS({"u", "songid"}, {"u", "position_in_seconds"}), NULL, _dbus_seek_cb, 0},
|
|
{ "PlayId", ELDBUS_ARGS({"u", "songid"}), NULL, _dbus_playid_cb, 0},
|
|
{ "PlayPos", ELDBUS_ARGS({"u", "songpos"}), NULL, _dbus_play_cb, 0},
|
|
{ "QueueList", NULL, NULL, _dbus_queue_list_cb, 0},
|
|
{ "QueueListCached", NULL, ELDBUS_ARGS({"a(stusssisssii)", "array_of_songs"}), _dbus_queue_list_cached_cb, 0},
|
|
{ "QueueListRange", ELDBUS_ARGS({"i", "start"}, {"i", "num"}), NULL, _dbus_queue_list_cb, 0},
|
|
{ "QueueListCachedRange", ELDBUS_ARGS({"i", "start"}, {"i", "num"}), ELDBUS_ARGS({"a(stusssisssii)", "array_of_songs"}), _dbus_queue_list_cached_cb, 0},
|
|
{ "ClearList", NULL, NULL, _dbus_clear_cb, 0},
|
|
{ "DeleteListRange", ELDBUS_ARGS({"i", "start"}, {"i", "num"}), NULL, _dbus_delete_list_cb, 0},
|
|
{ "AddList", ELDBUS_ARGS({"s", "path"}), NULL, _dbus_add_list_cb, 0},
|
|
{ "AddIdList", ELDBUS_ARGS({"s", "path"}, {"u", "position"}), NULL, _dbus_addid_list_cb, 0},
|
|
{ "SavePlaylist", ELDBUS_ARGS({"s", "path"}), NULL, _dbus_save_playlist_cb, 0},
|
|
{ "AddToPlaylist", ELDBUS_ARGS({"s", "playlist"}, {"s", "uri"}), NULL, _dbus_addto_playlist_cb, 0},
|
|
{ "LoadPlaylist", ELDBUS_ARGS({"s", "path"}), NULL, _dbus_load_playlist_cb, 0},
|
|
{ "MoveList", ELDBUS_ARGS({"u", "start_pos"}, {"i", "number"}, {"u", "target_pos"}), NULL, _dbus_move_list_cb, 0},
|
|
{ "Repeat", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_repeat_cb, 0},
|
|
{ "Shuffle", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_shuffle_cb, 0},
|
|
{ "Single", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_single_cb, 0},
|
|
{ "Consume", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_consume_cb, 0},
|
|
{ "ListInfo", ELDBUS_ARGS({"s", "path"}), ELDBUS_ARGS({"a(iv)", "array_of_infos"}), _dbus_listinfo_cb, 0},
|
|
{ "Update", ELDBUS_ARGS({"s", "path"}), NULL, _dbus_update_cb, 0},
|
|
{ "Find", ELDBUS_ARGS({"u", "search type"}, {"s", "param"}), ELDBUS_ARGS({"a(iv)", "array_of_infos"}), _dbus_find_cb, 0},
|
|
{NULL, NULL, NULL, NULL, 0}
|
|
};
|
|
|
|
static const Eldbus_Service_Interface_Desc base_desc =
|
|
{
|
|
EMPDD_METHOD_BASE, empd_methods, empd_signals, NULL, NULL, NULL
|
|
};
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct mpd_settings *settings = NULL;
|
|
const char *h, *pass, *p = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < argc; i++)
|
|
if (argv[i][0] == '-')
|
|
{
|
|
fprintf(stderr, "USAGE: %s <HOST<:PORT>> <PORT>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
pass = getenv("MPD_PASSWORD");
|
|
p = getenv("MPD_PORT");
|
|
if (argc < 2)
|
|
{
|
|
int port = DEFAULT_PORT;
|
|
|
|
h = getenv("MPD_HOST");
|
|
if (h)
|
|
{
|
|
if (!p)
|
|
p = strchr(h, ':');
|
|
if (p)
|
|
{
|
|
char host[PATH_MAX] = {0};
|
|
|
|
if (p - h > PATH_MAX - 1)
|
|
{
|
|
fprintf(stderr, "MPD_HOST longer than 4096 chars? I don't believe you.\n");
|
|
exit(-1);
|
|
}
|
|
port = strtol(p + 1, NULL, 10);
|
|
memcpy(host, h, p - h);
|
|
settings = mpd_settings_new(host, port, 3000, NULL, pass);
|
|
}
|
|
else
|
|
settings = mpd_settings_new(h, port, 3000, NULL, pass);
|
|
}
|
|
else
|
|
{
|
|
struct stat st;
|
|
|
|
if (!stat("/var/lib/mpd/socket", &st))
|
|
settings = mpd_settings_new("/var/lib/mpd/socket", p ? strtol(p, NULL, 10) : port, 3000, NULL, pass);
|
|
else if (!stat("/run/mpd/socket", &st))
|
|
settings = mpd_settings_new("/run/mpd/socket", p ? strtol(p, NULL, 10) : port, 3000, NULL, pass);
|
|
}
|
|
}
|
|
else
|
|
settings = mpd_settings_new(argv[1], atoi(argv[2]), 3000, NULL, pass);
|
|
|
|
empd = calloc(1, sizeof(EMPD));
|
|
empd->settings = settings;
|
|
ecore_app_no_system_modules();
|
|
ecore_con_init();
|
|
eldbus_init();
|
|
ecore_app_args_set(argc, (const char**)argv);
|
|
|
|
{
|
|
const char *t;
|
|
t = getenv("EINA_MEMPOOL");
|
|
if ((!t) || (!t[0])) t = "chained_mempool";
|
|
|
|
slist_mempool = eina_mempool_add(t, "E_Slist", NULL, sizeof(E_Slist), 64);
|
|
if (!slist_mempool)
|
|
slist_mempool = eina_mempool_add("pass_through", "E_Slist", NULL, sizeof(E_Slist), 64);
|
|
}
|
|
|
|
dbus_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION);
|
|
eldbus_name_request(dbus_conn, EMPDD_METHOD_BASE, 0, _dbus_request_name_cb, NULL);
|
|
empd_iface = eldbus_service_interface_register(dbus_conn, "/", &base_desc);
|
|
|
|
E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_SERVER_DEL, del, NULL);
|
|
E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_SERVER_DATA, data_cb, NULL);
|
|
|
|
if (settings)
|
|
reconnect(NULL, -1, NULL);
|
|
ecore_main_loop_begin();
|
|
|
|
return 0;
|
|
(void)cmd_txt;
|
|
}
|