1
0
mirror of https://github.com/SuperBFG7/ympd synced 2024-11-27 07:17:16 +00:00

removes old Makefile, reindent, fixups

This commit is contained in:
Andrew Karpow 2013-11-09 02:07:03 +01:00
parent 25f77b71ce
commit 062810144c
7 changed files with 449 additions and 443 deletions

View File

@ -12,6 +12,7 @@ SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
FIND_PACKAGE(LibWebSockets REQUIRED) FIND_PACKAGE(LibWebSockets REQUIRED)
FIND_PACKAGE(LibMPDClient REQUIRED) FIND_PACKAGE(LibMPDClient REQUIRED)
INCLUDE(CheckCSourceCompiles) INCLUDE(CheckCSourceCompiles)
INCLUDE(CPack)
SET(SOURCES SET(SOURCES
src/ympd.c src/ympd.c
@ -20,7 +21,7 @@ SET(SOURCES
) )
ADD_EXECUTABLE(ympd ${SOURCES}) ADD_EXECUTABLE(ympd ${SOURCES})
ADD_DEFINITIONS(-DDATADIR="${CMAKE_INSTALL_PREFIX}/share") ADD_DEFINITIONS(-DDATADIR="${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}")
TARGET_LINK_LIBRARIES(ympd ${LIBMPDCLIENT_LIBRARY} ${LIBWEBSOCKETS_LIBRARIES}) TARGET_LINK_LIBRARIES(ympd ${LIBMPDCLIENT_LIBRARY} ${LIBWEBSOCKETS_LIBRARIES})

View File

@ -1,13 +0,0 @@
CFLAGS = -Wall -Os
LFLAGS = `pkg-config --libs libwebsockets libmpdclient`
.PHONY: clean
ympd: main.o http_server.o mpd_client.o
$(CC) $(LFLAGS) $^ -o $@
%.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
clean:
rm -f main.o http_server.o mpd_client.o ympd

View File

@ -7,58 +7,58 @@
char *resource_path = LOCAL_RESOURCE_PATH; char *resource_path = LOCAL_RESOURCE_PATH;
struct serveable { struct serveable {
const char *urlpath; const char *urlpath;
const char *mimetype; const char *mimetype;
}; };
static const struct serveable whitelist[] = { static const struct serveable whitelist[] = {
{ "/css/bootstrap.css", "text/css" }, { "/css/bootstrap.css", "text/css" },
{ "/css/slider.css", "text/css" }, { "/css/slider.css", "text/css" },
{ "/css/mpd.css", "text/css" }, { "/css/mpd.css", "text/css" },
{ "/js/bootstrap.min.js", "text/javascript" }, { "/js/bootstrap.min.js", "text/javascript" },
{ "/js/mpd.js", "text/javascript" }, { "/js/mpd.js", "text/javascript" },
{ "/js/jquery-1.10.2.min.js", "text/javascript" }, { "/js/jquery-1.10.2.min.js", "text/javascript" },
{ "/js/bootstrap-slider.js", "text/javascript" }, { "/js/bootstrap-slider.js", "text/javascript" },
{ "/js/sammy.js", "text/javascript" }, { "/js/sammy.js", "text/javascript" },
{ "/fonts/glyphicons-halflings-regular.woff", "application/x-font-woff"}, { "/fonts/glyphicons-halflings-regular.woff", "application/x-font-woff"},
{ "/fonts/glyphicons-halflings-regular.svg", "image/svg+xml"}, { "/fonts/glyphicons-halflings-regular.svg", "image/svg+xml"},
{ "/fonts/glyphicons-halflings-regular.ttf", "application/x-font-ttf"}, { "/fonts/glyphicons-halflings-regular.ttf", "application/x-font-ttf"},
{ "/fonts/glyphicons-halflings-regular.eot", "application/vnd.ms-fontobject"}, { "/fonts/glyphicons-halflings-regular.eot", "application/vnd.ms-fontobject"},
/* last one is the default served if no match */ /* last one is the default served if no match */
{ "/index.html", "text/html" }, { "/index.html", "text/html" },
}; };
int callback_http(struct libwebsocket_context *context, int callback_http(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user, enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len) void *in, size_t len)
{ {
char buf[256]; char buf[256];
size_t n; size_t n;
switch (reason) { switch (reason) {
case LWS_CALLBACK_HTTP: case LWS_CALLBACK_HTTP:
for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++) for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
{ {
if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0) if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
break; break;
} }
sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath); sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath);
if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype)) if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype))
return -1; /* through completion or error, close the socket */ return -1; /* through completion or error, close the socket */
break; break;
case LWS_CALLBACK_HTTP_FILE_COMPLETION: case LWS_CALLBACK_HTTP_FILE_COMPLETION:
/* kill the connection after we sent one file */ /* kill the connection after we sent one file */
return -1; return -1;
default: default:
break; break;
} }
return 0; return 0;
} }

View File

@ -1,10 +1,11 @@
#include <libwebsockets.h> #include <libwebsockets.h>
struct per_session_data__http { struct per_session_data__http {
int fd; int fd;
}; };
int callback_http(struct libwebsocket_context *context, int callback_http(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user, enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len); void *in, size_t len);

View File

@ -14,7 +14,7 @@
#include "mpd_client.h" #include "mpd_client.h"
#define MAX_SIZE 9000*10 #define MAX_SIZE 1024 * 100
struct mpd_connection *conn = NULL; struct mpd_connection *conn = NULL;
enum mpd_conn_states mpd_conn_state = MPD_DISCONNECTED; enum mpd_conn_states mpd_conn_state = MPD_DISCONNECTED;
@ -22,359 +22,350 @@ enum mpd_state mpd_play_state = MPD_STATE_UNKNOWN;
unsigned queue_version; unsigned queue_version;
int callback_ympd(struct libwebsocket_context *context, int callback_ympd(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len) void *user, void *in, size_t len)
{ {
size_t n; size_t n;
int m; int m;
char *buf = NULL, *p; char *buf = NULL, *p;
struct per_session_data__ympd *pss = (struct per_session_data__ympd *)user; struct per_session_data__ympd *pss = (struct per_session_data__ympd *)user;
switch (reason) { switch (reason) {
case LWS_CALLBACK_ESTABLISHED: case LWS_CALLBACK_ESTABLISHED:
lwsl_info("mpd_client: " lwsl_info("mpd_client: "
"LWS_CALLBACK_ESTABLISHED\n"); "LWS_CALLBACK_ESTABLISHED\n");
break; break;
case LWS_CALLBACK_SERVER_WRITEABLE: case LWS_CALLBACK_SERVER_WRITEABLE:
buf = (char *)malloc(MAX_SIZE + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING); buf = (char *)malloc(MAX_SIZE + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING);
if(buf == NULL) { if(buf == NULL) {
lwsl_err("ERROR Failed allocating memory\n"); lwsl_err("ERROR Failed allocating memory\n");
return -1; return -1;
} }
p = &buf[LWS_SEND_BUFFER_PRE_PADDING]; p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
if(mpd_conn_state != MPD_CONNECTED) { if(mpd_conn_state != MPD_CONNECTED) {
n = snprintf(p, MAX_SIZE, "{\"type\":\"disconnected\"}"); n = snprintf(p, MAX_SIZE, "{\"type\":\"disconnected\"}");
} }
else if((pss->queue_version != queue_version) || (pss->do_send & DO_SEND_PLAYLIST)) { else if((pss->queue_version != queue_version) || (pss->do_send & DO_SEND_PLAYLIST)) {
n = mpd_put_playlist(p); n = mpd_put_playlist(p);
pss->queue_version = queue_version; pss->queue_version = queue_version;
pss->do_send &= ~DO_SEND_PLAYLIST; pss->do_send &= ~DO_SEND_PLAYLIST;
} }
else if(pss->do_send & DO_SEND_TRACK_INFO) { else if(pss->do_send & DO_SEND_TRACK_INFO) {
n = mpd_put_current_song(p); n = mpd_put_current_song(p);
pss->do_send &= ~DO_SEND_TRACK_INFO; pss->do_send &= ~DO_SEND_TRACK_INFO;
} }
else if(pss->do_send & DO_SEND_BROWSE) { else if(pss->do_send & DO_SEND_BROWSE) {
n = mpd_put_browse(p, pss->browse_path); n = mpd_put_browse(p, pss->browse_path);
pss->do_send &= ~DO_SEND_BROWSE; pss->do_send &= ~DO_SEND_BROWSE;
free(pss->browse_path); free(pss->browse_path);
} }
else { else
n = mpd_put_state(p); n = mpd_put_state(p);
}
if(n > 0) if(n > 0)
m = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT); m = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT);
if(p != NULL) if (m < n) {
printf("Sending out: %s\n", p); lwsl_err("ERROR %d writing to socket\n", n, m);
free(buf);
return -1;
}
free(buf);
break;
if (m < n) { case LWS_CALLBACK_RECEIVE:
lwsl_err("ERROR %d writing to socket\n", n, m); if(!strcmp((const char *)in, MPD_API_GET_PLAYLIST))
free(buf); pss->do_send |= DO_SEND_PLAYLIST;
return -1; else if(!strcmp((const char *)in, MPD_API_GET_TRACK_INFO))
} pss->do_send |= DO_SEND_TRACK_INFO;
free(buf); else if(!strcmp((const char *)in, MPD_API_UPDATE_DB)) {
break; mpd_send_update(conn, NULL);
mpd_response_finish(conn);
}
else if(!strcmp((const char *)in, MPD_API_SET_PAUSE)) {
mpd_send_toggle_pause(conn);
mpd_response_finish(conn);
}
else if(!strcmp((const char *)in, MPD_API_SET_PREV)) {
mpd_send_previous(conn);
mpd_response_finish(conn);
}
else if(!strcmp((const char *)in, MPD_API_SET_NEXT)) {
mpd_send_next(conn);
mpd_response_finish(conn);
}
else if(!strcmp((const char *)in, MPD_API_RM_ALL)) {
mpd_run_clear(conn);
}
else if(!strncmp((const char *)in, MPD_API_RM_TRACK, sizeof(MPD_API_RM_TRACK)-1)) {
unsigned id;
if(sscanf(in, "MPD_API_RM_TRACK,%d", &id))
mpd_run_delete_id(conn, id);
}
else if(!strncmp((const char *)in, MPD_API_PLAY_TRACK, sizeof(MPD_API_PLAY_TRACK)-1)) {
unsigned id;
if(sscanf(in, "MPD_API_PLAY_TRACK,%d", &id))
mpd_run_play_id(conn, id);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_RANDOM, sizeof(MPD_API_TOGGLE_RANDOM)-1)) {
unsigned random;
if(sscanf(in, "MPD_API_TOGGLE_RANDOM,%d", &random))
mpd_run_random(conn, random);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_REPEAT, sizeof(MPD_API_TOGGLE_REPEAT)-1)) {
unsigned repeat;
if(sscanf(in, "MPD_API_TOGGLE_REPEAT,%d", &repeat))
mpd_run_repeat(conn, repeat);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_CONSUME, sizeof(MPD_API_TOGGLE_CONSUME)-1)) {
unsigned consume;
if(sscanf(in, "MPD_API_TOGGLE_CONSUME,%d", &consume))
mpd_run_consume(conn, consume);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_SINGLE, sizeof(MPD_API_TOGGLE_SINGLE)-1)) {
unsigned single;
if(sscanf(in, "MPD_API_TOGGLE_SINGLE,%d", &single))
mpd_run_single(conn, single);
}
else if(!strncmp((const char *)in, MPD_API_SET_VOLUME, sizeof(MPD_API_SET_VOLUME)-1)) {
unsigned int volume;
if(sscanf(in, "MPD_API_SET_VOLUME,%ud", &volume) && volume < 100)
mpd_run_set_volume(conn, volume);
}
else if(!strncmp((const char *)in, MPD_API_GET_BROWSE, sizeof(MPD_API_GET_BROWSE)-1)) {
char *dir;
if(sscanf(in, "MPD_API_GET_BROWSE,%m[^\t\n]", &dir) && dir != NULL) {
pss->do_send |= DO_SEND_BROWSE;
pss->browse_path = dir;
}
}
else if(!strncmp((const char *)in, MPD_API_ADD_TRACK, sizeof(MPD_API_ADD_TRACK)-1)) {
char *uri;
if(sscanf(in, "MPD_API_ADD_TRACK,%m[^\t\n]", &uri) && uri != NULL) {
mpd_run_add(conn, uri);
free(uri);
}
}
break;
case LWS_CALLBACK_RECEIVE: default:
printf("Got %s\n", (char *)in); break;
}
if(!strcmp((const char *)in, MPD_API_GET_PLAYLIST)) return 0;
pss->do_send |= DO_SEND_PLAYLIST;
else if(!strcmp((const char *)in, MPD_API_GET_TRACK_INFO))
pss->do_send |= DO_SEND_TRACK_INFO;
else if(!strcmp((const char *)in, MPD_API_UPDATE_DB)) {
mpd_send_update(conn, NULL);
mpd_response_finish(conn);
}
else if(!strcmp((const char *)in, MPD_API_SET_PAUSE)) {
mpd_send_toggle_pause(conn);
mpd_response_finish(conn);
}
else if(!strcmp((const char *)in, MPD_API_SET_PREV)) {
mpd_send_previous(conn);
mpd_response_finish(conn);
}
else if(!strcmp((const char *)in, MPD_API_SET_NEXT)) {
mpd_send_next(conn);
mpd_response_finish(conn);
}
else if(!strcmp((const char *)in, MPD_API_RM_ALL)) {
mpd_run_clear(conn);
}
else if(!strncmp((const char *)in, MPD_API_RM_TRACK, sizeof(MPD_API_RM_TRACK)-1)) {
unsigned id;
if(sscanf(in, "MPD_API_RM_TRACK,%d", &id))
mpd_run_delete_id(conn, id);
}
else if(!strncmp((const char *)in, MPD_API_PLAY_TRACK, sizeof(MPD_API_PLAY_TRACK)-1)) {
unsigned id;
if(sscanf(in, "MPD_API_PLAY_TRACK,%d", &id))
mpd_run_play_id(conn, id);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_RANDOM, sizeof(MPD_API_TOGGLE_RANDOM)-1)) {
unsigned random;
if(sscanf(in, "MPD_API_TOGGLE_RANDOM,%d", &random))
mpd_run_random(conn, random);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_REPEAT, sizeof(MPD_API_TOGGLE_REPEAT)-1)) {
unsigned repeat;
if(sscanf(in, "MPD_API_TOGGLE_REPEAT,%d", &repeat))
mpd_run_repeat(conn, repeat);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_CONSUME, sizeof(MPD_API_TOGGLE_CONSUME)-1)) {
unsigned consume;
if(sscanf(in, "MPD_API_TOGGLE_CONSUME,%d", &consume))
mpd_run_consume(conn, consume);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_SINGLE, sizeof(MPD_API_TOGGLE_SINGLE)-1)) {
unsigned single;
if(sscanf(in, "MPD_API_TOGGLE_SINGLE,%d", &single))
mpd_run_single(conn, single);
}
else if(!strncmp((const char *)in, MPD_API_SET_VOLUME, sizeof(MPD_API_SET_VOLUME)-1)) {
unsigned int volume;
if(sscanf(in, "MPD_API_SET_VOLUME,%ud", &volume) && volume < 100)
mpd_run_set_volume(conn, volume);
}
else if(!strncmp((const char *)in, MPD_API_GET_BROWSE, sizeof(MPD_API_GET_BROWSE)-1)) {
char *dir;
if(sscanf(in, "MPD_API_GET_BROWSE,%m[^\t\n]", &dir) && dir != NULL) {
printf("sending '%s'\n", dir);
pss->do_send |= DO_SEND_BROWSE;
pss->browse_path = dir;
}
}
else if(!strncmp((const char *)in, MPD_API_ADD_TRACK, sizeof(MPD_API_ADD_TRACK)-1)) {
char *uri;
if(sscanf(in, "MPD_API_ADD_TRACK,%m[^\t\n]", &uri) && uri != NULL) {
printf("sending '%s'\n", uri);
mpd_run_add(conn, uri);
free(uri);
}
}
break;
default:
break;
}
return 0;
} }
void mpd_loop() void mpd_loop()
{ {
switch (mpd_conn_state) { switch (mpd_conn_state) {
case MPD_DISCONNECTED: case MPD_DISCONNECTED:
/* Try to connect */ /* Try to connect */
conn = mpd_connection_new("127.0.0.1", 6600, 3000); conn = mpd_connection_new("127.0.0.1", 6600, 3000);
if (conn == NULL) { if (conn == NULL) {
lwsl_err("Out of memory."); lwsl_err("Out of memory.");
mpd_conn_state = MPD_FAILURE; mpd_conn_state = MPD_FAILURE;
return; return;
} }
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
lwsl_notice("MPD connection: %s\n", mpd_connection_get_error_message(conn)); lwsl_notice("MPD connection: %s\n", mpd_connection_get_error_message(conn));
mpd_conn_state = MPD_FAILURE; mpd_conn_state = MPD_FAILURE;
return; return;
} }
lwsl_notice("MPD connected.\n"); lwsl_notice("MPD connected.\n");
mpd_conn_state = MPD_CONNECTED; mpd_conn_state = MPD_CONNECTED;
break; break;
case MPD_FAILURE: case MPD_FAILURE:
lwsl_notice("MPD connection failed.\n"); lwsl_notice("MPD connection failed.\n");
if(conn != NULL) if(conn != NULL)
mpd_connection_free(conn); mpd_connection_free(conn);
conn = NULL; conn = NULL;
mpd_conn_state = MPD_DISCONNECTED; mpd_conn_state = MPD_DISCONNECTED;
break; break;
case MPD_CONNECTED: case MPD_CONNECTED:
/* Nothing to do */ /* Nothing to do */
break; break;
} }
} }
char* mpd_get_title(struct mpd_song const *song) char* mpd_get_title(struct mpd_song const *song)
{ {
char *str, *ptr; char *str, *ptr;
str = (char *)mpd_song_get_tag(song, MPD_TAG_TITLE, 0); str = (char *)mpd_song_get_tag(song, MPD_TAG_TITLE, 0);
if(str == NULL) if(str == NULL)
str = (char *)mpd_song_get_uri(song); str = (char *)mpd_song_get_uri(song);
if(str == NULL) if(str == NULL)
return NULL; return NULL;
ptr = str; ptr = str;
while(*ptr++ != '\0') while(*ptr++ != '\0')
if(*ptr=='"') if(*ptr=='"')
*ptr='\''; *ptr='\'';
return basename(str); return basename(str);
} }
int mpd_put_state(char *buffer) int mpd_put_state(char *buffer)
{ {
struct mpd_status *status; struct mpd_status *status;
int len; int len;
status = mpd_run_status(conn); status = mpd_run_status(conn);
if (!status) { if (!status) {
lwsl_err("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(conn)); lwsl_err("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(conn));
mpd_conn_state = MPD_FAILURE; mpd_conn_state = MPD_FAILURE;
return 0; return 0;
} }
len = snprintf(buffer, MAX_SIZE, len = snprintf(buffer, MAX_SIZE,
"{\"type\":\"state\", \"data\":{" "{\"type\":\"state\", \"data\":{"
" \"state\":%d, \"volume\":%d, \"repeat\":%d," " \"state\":%d, \"volume\":%d, \"repeat\":%d,"
" \"single\":%d, \"consume\":%d, \"random\":%d, " " \"single\":%d, \"consume\":%d, \"random\":%d, "
" \"songpos\": %d, \"elapsedTime\": %d, \"totalTime\":%d, " " \"songpos\": %d, \"elapsedTime\": %d, \"totalTime\":%d, "
" \"currentsongid\": %d" " \"currentsongid\": %d"
"}}", "}}",
mpd_status_get_state(status), mpd_status_get_state(status),
mpd_status_get_volume(status), mpd_status_get_volume(status),
mpd_status_get_repeat(status), mpd_status_get_repeat(status),
mpd_status_get_single(status), mpd_status_get_single(status),
mpd_status_get_consume(status), mpd_status_get_consume(status),
mpd_status_get_random(status), mpd_status_get_random(status),
mpd_status_get_song_pos(status), mpd_status_get_song_pos(status),
mpd_status_get_elapsed_time(status), mpd_status_get_elapsed_time(status),
mpd_status_get_total_time(status), mpd_status_get_total_time(status),
mpd_status_get_song_id(status)); mpd_status_get_song_id(status));
queue_version = mpd_status_get_queue_version(status); queue_version = mpd_status_get_queue_version(status);
mpd_status_free(status); mpd_status_free(status);
return len; return len;
} }
int mpd_put_current_song(char *buffer) int mpd_put_current_song(char *buffer)
{ {
struct mpd_song *song; struct mpd_song *song;
int len; int len;
song = mpd_run_current_song(conn); song = mpd_run_current_song(conn);
if(song == NULL) if(song == NULL)
return 0; return 0;
len = snprintf(buffer, MAX_SIZE, "{\"type\": \"current_song\", \"data\":" len = snprintf(buffer, MAX_SIZE, "{\"type\": \"current_song\", \"data\":"
"{\"pos\":%d, \"title\":\"%s\", \"artist\":\"%s\", \"album\":\"%s\"}}", "{\"pos\":%d, \"title\":\"%s\", \"artist\":\"%s\", \"album\":\"%s\"}}",
mpd_song_get_pos(song), mpd_song_get_pos(song),
mpd_get_title(song), mpd_get_title(song),
mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), mpd_song_get_tag(song, MPD_TAG_ARTIST, 0),
mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) mpd_song_get_tag(song, MPD_TAG_ALBUM, 0)
); );
mpd_song_free(song); mpd_song_free(song);
mpd_response_finish(conn); mpd_response_finish(conn);
return len; return len;
} }
int mpd_put_playlist(char *buffer) int mpd_put_playlist(char *buffer)
{ {
char *cur = buffer; char *cur = buffer;
const char *end = buffer + MAX_SIZE; const char *end = buffer + MAX_SIZE;
struct mpd_entity *entity; struct mpd_entity *entity;
if (!mpd_send_list_queue_meta(conn)) { if (!mpd_send_list_queue_meta(conn)) {
lwsl_err("MPD mpd_send_list_queue_meta: %s\n", mpd_connection_get_error_message(conn)); lwsl_err("MPD mpd_send_list_queue_meta: %s\n", mpd_connection_get_error_message(conn));
mpd_conn_state = MPD_FAILURE; mpd_conn_state = MPD_FAILURE;
return 0; return 0;
} }
cur += snprintf(cur, end - cur, "{\"type\": \"playlist\", \"data\": [ "); cur += snprintf(cur, end - cur, "{\"type\": \"playlist\", \"data\": [ ");
while((entity = mpd_recv_entity(conn)) != NULL) { while((entity = mpd_recv_entity(conn)) != NULL) {
const struct mpd_song *song; const struct mpd_song *song;
if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
song = mpd_entity_get_song(entity); song = mpd_entity_get_song(entity);
cur += snprintf(cur, end - cur, cur += snprintf(cur, end - cur,
"{\"id\":%d, \"pos\":%d, \"duration\":%d, \"title\":\"%s\"},", "{\"id\":%d, \"pos\":%d, \"duration\":%d, \"title\":\"%s\"},",
mpd_song_get_id(song), mpd_song_get_id(song),
mpd_song_get_pos(song), mpd_song_get_pos(song),
mpd_song_get_duration(song), mpd_song_get_duration(song),
mpd_get_title(song) mpd_get_title(song)
); );
} }
mpd_entity_free(entity); mpd_entity_free(entity);
} }
/* remove last ',' */ /* remove last ',' */
cur--; cur--;
cur += snprintf(cur, end - cur, "] }"); cur += snprintf(cur, end - cur, "] }");
return cur - buffer; return cur - buffer;
} }
int mpd_put_browse(char *buffer, char *path) int mpd_put_browse(char *buffer, char *path)
{ {
char *cur = buffer; char *cur = buffer;
const char *end = buffer + MAX_SIZE; const char *end = buffer + MAX_SIZE;
struct mpd_entity *entity; struct mpd_entity *entity;
if (!mpd_send_list_meta(conn, path)) { if (!mpd_send_list_meta(conn, path)) {
lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn)); lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn));
mpd_conn_state = MPD_FAILURE; mpd_conn_state = MPD_FAILURE;
return 0; return 0;
} }
cur += snprintf(cur, end - cur, "{\"type\":\"browse\",\"data\":[ "); cur += snprintf(cur, end - cur, "{\"type\":\"browse\",\"data\":[ ");
while((entity = mpd_recv_entity(conn)) != NULL) { while((entity = mpd_recv_entity(conn)) != NULL) {
const struct mpd_song *song; const struct mpd_song *song;
const struct mpd_directory *dir; const struct mpd_directory *dir;
const struct mpd_playlist *pl; const struct mpd_playlist *pl;
switch (mpd_entity_get_type(entity)) { switch (mpd_entity_get_type(entity)) {
case MPD_ENTITY_TYPE_UNKNOWN: case MPD_ENTITY_TYPE_UNKNOWN:
break; break;
case MPD_ENTITY_TYPE_SONG: case MPD_ENTITY_TYPE_SONG:
song = mpd_entity_get_song(entity); song = mpd_entity_get_song(entity);
cur += snprintf(cur, end - cur, cur += snprintf(cur, end - cur,
"{\"type\":\"song\",\"uri\":\"%s\",\"duration\":%d,\"title\":\"%s\"},", "{\"type\":\"song\",\"uri\":\"%s\",\"duration\":%d,\"title\":\"%s\"},",
mpd_song_get_uri(song), mpd_song_get_uri(song),
mpd_song_get_duration(song), mpd_song_get_duration(song),
mpd_get_title(song) mpd_get_title(song)
); );
break; break;
case MPD_ENTITY_TYPE_DIRECTORY: case MPD_ENTITY_TYPE_DIRECTORY:
dir = mpd_entity_get_directory(entity); dir = mpd_entity_get_directory(entity);
cur += snprintf(cur, end - cur, cur += snprintf(cur, end - cur,
"{\"type\":\"directory\",\"dir\":\"%s\"},", "{\"type\":\"directory\",\"dir\":\"%s\"},",
mpd_directory_get_path(dir) mpd_directory_get_path(dir)
); );
break; break;
case MPD_ENTITY_TYPE_PLAYLIST: case MPD_ENTITY_TYPE_PLAYLIST:
pl = mpd_entity_get_playlist(entity); pl = mpd_entity_get_playlist(entity);
cur += snprintf(cur, end - cur, cur += snprintf(cur, end - cur,
"{\"type\":\"playlist\",\"plist\":\"%s\"},", "{\"type\":\"playlist\",\"plist\":\"%s\"},",
mpd_playlist_get_path(pl) mpd_playlist_get_path(pl)
); );
break; break;
} }
mpd_entity_free(entity); mpd_entity_free(entity);
} }
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(conn)) { if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(conn)) {
lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn)); lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn));
mpd_conn_state = MPD_FAILURE; mpd_conn_state = MPD_FAILURE;
return 0; return 0;
} }
/* remove last ',' */ /* remove last ',' */
cur--; cur--;
cur += snprintf(cur, end - cur, "] }"); cur += snprintf(cur, end - cur, "] }");
return cur - buffer; return cur - buffer;
} }

View File

@ -5,21 +5,6 @@
#define DO_SEND_TRACK_INFO (1 << 2) #define DO_SEND_TRACK_INFO (1 << 2)
#define DO_SEND_BROWSE (1 << 3) #define DO_SEND_BROWSE (1 << 3)
struct libwebsocket_protocols *protocol_array;
struct per_session_data__ympd {
int do_send;
unsigned queue_version;
char *browse_path;
};
enum mpd_conn_states {
MPD_FAILURE,
MPD_DISCONNECTED,
MPD_CONNECTED
};
#define MPD_API_GET_SEEK "MPD_API_GET_SEEK" #define MPD_API_GET_SEEK "MPD_API_GET_SEEK"
#define MPD_API_GET_PLAYLIST "MPD_API_GET_PLAYLIST" #define MPD_API_GET_PLAYLIST "MPD_API_GET_PLAYLIST"
#define MPD_API_GET_TRACK_INFO "MPD_API_GET_TRACK_INFO" #define MPD_API_GET_TRACK_INFO "MPD_API_GET_TRACK_INFO"
@ -41,13 +26,22 @@ enum mpd_conn_states {
#define MPD_API_TOGGLE_SINGLE "MPD_API_TOGGLE_SINGLE" #define MPD_API_TOGGLE_SINGLE "MPD_API_TOGGLE_SINGLE"
#define MPD_API_TOGGLE_REPEAT "MPD_API_TOGGLE_REPEAT" #define MPD_API_TOGGLE_REPEAT "MPD_API_TOGGLE_REPEAT"
struct per_session_data__ympd {
int do_send;
unsigned queue_version;
char *browse_path;
};
enum mpd_conn_states {
MPD_FAILURE,
MPD_DISCONNECTED,
MPD_CONNECTED
};
int callback_ympd(struct libwebsocket_context *context, int callback_ympd(struct libwebsocket_context *context,
struct libwebsocket *wsi, struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len); void *user, void *in, size_t len);
void mpd_loop(); void mpd_loop();
int mpd_put_state(char *buffer); int mpd_put_state(char *buffer);
int mpd_put_current_song(char *buffer); int mpd_put_current_song(char *buffer);

View File

@ -2,109 +2,141 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <sys/time.h> #include <sys/time.h>
#include "http_server.h" #include "http_server.h"
#include "mpd_client.h" #include "mpd_client.h"
struct libwebsocket_protocols protocols[] = { extern char *optarg;
/* first protocol must always be HTTP handler */ extern char *resource_path;
{
"http-only", /* name */
callback_http, /* callback */
sizeof (struct per_session_data__http), /* per_session_data_size */
0, /* max frame size / rx buffer */
},
{
"ympd-client",
callback_ympd,
sizeof(struct per_session_data__ympd),
255,
},
{ NULL, NULL, 0, 0 } /* terminator */ struct libwebsocket_protocols protocols[] = {
/* first protocol must always be HTTP handler */
{
"http-only", /* name */
callback_http, /* callback */
sizeof (struct per_session_data__http), /* per_session_data_size */
0, /* max frame size / rx buffer */
0, /* no_buffer_all_partial_tx */
},
{
"ympd-client",
callback_ympd,
sizeof(struct per_session_data__ympd),
255,
0,
},
{ NULL, NULL, 0, 0, 0 } /* terminator */
}; };
int force_exit = 0; int force_exit = 0;
void bye() void bye()
{ {
force_exit = 1; force_exit = 1;
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int n = 0; int n, gid = -1, uid = -1;
struct libwebsocket_context *context; struct libwebsocket_context *context;
int opts = 0; const char *cert_filepath = NULL;
char interface_name[128] = ""; const char *private_key_filepath = NULL;
const char *iface = NULL; const char *iface = NULL;
struct lws_context_creation_info info; struct lws_context_creation_info info;
unsigned int oldus = 0; unsigned int oldus = 0;
protocol_array = protocols;
atexit(bye); atexit(bye);
memset(&info, 0, sizeof info); memset(&info, 0, sizeof info);
info.port = 7681; info.port = 80;
while (n >= 0) { while((n = getopt(argc, argv, "i:p:r:c:k:g:u:h")) != -1) {
n = getopt(argc, argv, "i:p:"); switch (n) {
if (n < 0) case 'i':
continue; iface = optarg;
switch (n) { break;
case 'p': case 'p':
info.port = atoi(optarg); info.port = atoi(optarg);
break; break;
case 'i': case 'r':
strncpy(interface_name, optarg, sizeof interface_name); resource_path = optarg;
interface_name[(sizeof interface_name) - 1] = '\0'; break;
iface = interface_name; case 'c':
break; cert_filepath = optarg;
case 'h': break;
fprintf(stderr, "Usage: %s [OPTION]...\n" case 'k':
"[-=<p>] [--mpd-port=<P>] " private_key_filepath = optarg;
"[-i <log bitfield>] " break;
"[--resource_path <path>]\n", argv[0]); case 'g':
exit(1); gid = atoi(optarg);
} break;
} case 'u':
uid = atoi(optarg);
break;
case '?':
case 'h':
lwsl_err("Usage: %s [OPTION]...\n"
"\t[-p <port>]\n"
"\t[-i <interface>]\n"
"\t[-r <htdocs path>]\n"
"\t[-c <ssl certificate filepath>]\n"
"\t[-k <ssl private key filepath>]\n"
"\t[-g <group id after socket bind>]\n"
"\t[-u <user id after socket bind>]\n"
"\t[-h]\n"
, argv[0]);
return EXIT_FAILURE;
}
}
info.iface = iface; if(cert_filepath != NULL && private_key_filepath == NULL) {
info.protocols = protocols; lwsl_err("private key filepath needed\n");
info.extensions = libwebsocket_get_internal_extensions(); return EXIT_FAILURE;
info.gid = -1; }
info.uid = -1;
info.ssl_cert_filepath = NULL;
info.ssl_private_key_filepath = NULL;
info.options = opts;
context = libwebsocket_create_context(&info); if(private_key_filepath != NULL && cert_filepath == NULL) {
if (context == NULL) { lwsl_err("public cert filepath needed\n");
lwsl_err("libwebsocket init failed\n"); return EXIT_FAILURE;
return EXIT_FAILURE; }
}
n = 0; info.ssl_cert_filepath = cert_filepath;
while (n >= 0 && !force_exit) { info.ssl_private_key_filepath = private_key_filepath;
struct timeval tv; info.iface = iface;
info.protocols = protocols;
info.extensions = libwebsocket_get_internal_extensions();
info.gid = gid;
info.uid = uid;
info.options = 0;
gettimeofday(&tv, NULL); context = libwebsocket_create_context(&info);
if (context == NULL) {
lwsl_err("libwebsocket init failed\n");
return EXIT_FAILURE;
}
/* n = 0;
* This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every while (n >= 0 && !force_exit) {
* live websocket connection using the DUMB_INCREMENT protocol, struct timeval tv;
* as soon as it can take more packets (usually immediately)
*/
if (((unsigned int)tv.tv_usec - oldus) > 1000 * 500) { gettimeofday(&tv, NULL);
mpd_loop();
libwebsocket_callback_on_writable_all_protocol(&protocols[1]);
oldus = tv.tv_usec;
}
n = libwebsocket_service(context, 50); /*
} * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
* live websocket connection using the DUMB_INCREMENT protocol,
* as soon as it can take more packets (usually immediately)
*/
libwebsocket_context_destroy(context); if (((unsigned int)tv.tv_usec - oldus) > 1000 * 500) {
return 0; mpd_loop();
libwebsocket_callback_on_writable_all_protocol(&protocols[1]);
oldus = tv.tv_usec;
}
n = libwebsocket_service(context, 50);
}
libwebsocket_context_destroy(context);
return 0;
} }