diff --git a/CMakeLists.txt b/CMakeLists.txt index b8ff586..008da22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") FIND_PACKAGE(LibWebSockets REQUIRED) FIND_PACKAGE(LibMPDClient REQUIRED) INCLUDE(CheckCSourceCompiles) +INCLUDE(CPack) SET(SOURCES src/ympd.c @@ -20,7 +21,7 @@ SET(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}) diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 1bfb261..0000000 --- a/src/Makefile +++ /dev/null @@ -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 diff --git a/src/http_server.c b/src/http_server.c index 8a25271..efe6d97 100644 --- a/src/http_server.c +++ b/src/http_server.c @@ -7,58 +7,58 @@ char *resource_path = LOCAL_RESOURCE_PATH; struct serveable { - const char *urlpath; - const char *mimetype; + const char *urlpath; + const char *mimetype; }; static const struct serveable whitelist[] = { - { "/css/bootstrap.css", "text/css" }, - { "/css/slider.css", "text/css" }, - { "/css/mpd.css", "text/css" }, + { "/css/bootstrap.css", "text/css" }, + { "/css/slider.css", "text/css" }, + { "/css/mpd.css", "text/css" }, - { "/js/bootstrap.min.js", "text/javascript" }, - { "/js/mpd.js", "text/javascript" }, - { "/js/jquery-1.10.2.min.js", "text/javascript" }, - { "/js/bootstrap-slider.js", "text/javascript" }, - { "/js/sammy.js", "text/javascript" }, - - { "/fonts/glyphicons-halflings-regular.woff", "application/x-font-woff"}, - { "/fonts/glyphicons-halflings-regular.svg", "image/svg+xml"}, - { "/fonts/glyphicons-halflings-regular.ttf", "application/x-font-ttf"}, - { "/fonts/glyphicons-halflings-regular.eot", "application/vnd.ms-fontobject"}, + { "/js/bootstrap.min.js", "text/javascript" }, + { "/js/mpd.js", "text/javascript" }, + { "/js/jquery-1.10.2.min.js", "text/javascript" }, + { "/js/bootstrap-slider.js", "text/javascript" }, + { "/js/sammy.js", "text/javascript" }, - /* last one is the default served if no match */ - { "/index.html", "text/html" }, + { "/fonts/glyphicons-halflings-regular.woff", "application/x-font-woff"}, + { "/fonts/glyphicons-halflings-regular.svg", "image/svg+xml"}, + { "/fonts/glyphicons-halflings-regular.ttf", "application/x-font-ttf"}, + { "/fonts/glyphicons-halflings-regular.eot", "application/vnd.ms-fontobject"}, + + /* last one is the default served if no match */ + { "/index.html", "text/html" }, }; int callback_http(struct libwebsocket_context *context, - struct libwebsocket *wsi, - enum libwebsocket_callback_reasons reason, void *user, - void *in, size_t len) + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, void *user, + void *in, size_t len) { - char buf[256]; - size_t n; + char buf[256]; + size_t n; - switch (reason) { - case LWS_CALLBACK_HTTP: - for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++) - { - if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0) - break; - } - sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath); + switch (reason) { + case LWS_CALLBACK_HTTP: + for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++) + { + if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0) + break; + } + sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath); - if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype)) - return -1; /* through completion or error, close the socket */ + if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype)) + return -1; /* through completion or error, close the socket */ - break; + break; - case LWS_CALLBACK_HTTP_FILE_COMPLETION: - /* kill the connection after we sent one file */ - return -1; - default: - break; - } + case LWS_CALLBACK_HTTP_FILE_COMPLETION: + /* kill the connection after we sent one file */ + return -1; + default: + break; + } - return 0; + return 0; } diff --git a/src/http_server.h b/src/http_server.h index 7a956f9..bf60dde 100644 --- a/src/http_server.h +++ b/src/http_server.h @@ -1,10 +1,11 @@ #include struct per_session_data__http { - int fd; + int fd; }; int callback_http(struct libwebsocket_context *context, - struct libwebsocket *wsi, - enum libwebsocket_callback_reasons reason, void *user, - void *in, size_t len); + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, void *user, + void *in, size_t len); + diff --git a/src/mpd_client.c b/src/mpd_client.c index 7a4741a..27184d0 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -14,7 +14,7 @@ #include "mpd_client.h" -#define MAX_SIZE 9000*10 +#define MAX_SIZE 1024 * 100 struct mpd_connection *conn = NULL; 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; int callback_ympd(struct libwebsocket_context *context, - struct libwebsocket *wsi, - enum libwebsocket_callback_reasons reason, - void *user, void *in, size_t len) + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len) { - size_t n; - int m; - char *buf = NULL, *p; - struct per_session_data__ympd *pss = (struct per_session_data__ympd *)user; + size_t n; + int m; + char *buf = NULL, *p; + struct per_session_data__ympd *pss = (struct per_session_data__ympd *)user; - switch (reason) { - case LWS_CALLBACK_ESTABLISHED: - lwsl_info("mpd_client: " - "LWS_CALLBACK_ESTABLISHED\n"); - break; + switch (reason) { + case LWS_CALLBACK_ESTABLISHED: + lwsl_info("mpd_client: " + "LWS_CALLBACK_ESTABLISHED\n"); + break; - case LWS_CALLBACK_SERVER_WRITEABLE: - buf = (char *)malloc(MAX_SIZE + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING); - if(buf == NULL) { - lwsl_err("ERROR Failed allocating memory\n"); - return -1; - } - p = &buf[LWS_SEND_BUFFER_PRE_PADDING]; + case LWS_CALLBACK_SERVER_WRITEABLE: + buf = (char *)malloc(MAX_SIZE + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING); + if(buf == NULL) { + lwsl_err("ERROR Failed allocating memory\n"); + return -1; + } + p = &buf[LWS_SEND_BUFFER_PRE_PADDING]; - if(mpd_conn_state != MPD_CONNECTED) { - n = snprintf(p, MAX_SIZE, "{\"type\":\"disconnected\"}"); - } - else if((pss->queue_version != queue_version) || (pss->do_send & DO_SEND_PLAYLIST)) { - n = mpd_put_playlist(p); - pss->queue_version = queue_version; - pss->do_send &= ~DO_SEND_PLAYLIST; - } - else if(pss->do_send & DO_SEND_TRACK_INFO) { - n = mpd_put_current_song(p); - pss->do_send &= ~DO_SEND_TRACK_INFO; - } - else if(pss->do_send & DO_SEND_BROWSE) { - n = mpd_put_browse(p, pss->browse_path); - pss->do_send &= ~DO_SEND_BROWSE; - free(pss->browse_path); - } - else { - n = mpd_put_state(p); - } + if(mpd_conn_state != MPD_CONNECTED) { + n = snprintf(p, MAX_SIZE, "{\"type\":\"disconnected\"}"); + } + else if((pss->queue_version != queue_version) || (pss->do_send & DO_SEND_PLAYLIST)) { + n = mpd_put_playlist(p); + pss->queue_version = queue_version; + pss->do_send &= ~DO_SEND_PLAYLIST; + } + else if(pss->do_send & DO_SEND_TRACK_INFO) { + n = mpd_put_current_song(p); + pss->do_send &= ~DO_SEND_TRACK_INFO; + } + else if(pss->do_send & DO_SEND_BROWSE) { + n = mpd_put_browse(p, pss->browse_path); + pss->do_send &= ~DO_SEND_BROWSE; + free(pss->browse_path); + } + else + n = mpd_put_state(p); - if(n > 0) - m = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT); + if(n > 0) + m = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT); - if(p != NULL) - printf("Sending out: %s\n", p); + if (m < n) { + lwsl_err("ERROR %d writing to socket\n", n, m); + free(buf); + return -1; + } + free(buf); + break; - if (m < n) { - lwsl_err("ERROR %d writing to socket\n", n, m); - free(buf); - return -1; - } - free(buf); - break; + case LWS_CALLBACK_RECEIVE: + if(!strcmp((const char *)in, MPD_API_GET_PLAYLIST)) + 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) { + 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: - printf("Got %s\n", (char *)in); + default: + break; + } - if(!strcmp((const char *)in, MPD_API_GET_PLAYLIST)) - 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; + return 0; } void mpd_loop() { - switch (mpd_conn_state) { - case MPD_DISCONNECTED: - /* Try to connect */ - conn = mpd_connection_new("127.0.0.1", 6600, 3000); - if (conn == NULL) { - lwsl_err("Out of memory."); - mpd_conn_state = MPD_FAILURE; - return; - } + switch (mpd_conn_state) { + case MPD_DISCONNECTED: + /* Try to connect */ + conn = mpd_connection_new("127.0.0.1", 6600, 3000); + if (conn == NULL) { + lwsl_err("Out of memory."); + mpd_conn_state = MPD_FAILURE; + return; + } - if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { - lwsl_notice("MPD connection: %s\n", mpd_connection_get_error_message(conn)); - mpd_conn_state = MPD_FAILURE; - return; - } + if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) { + lwsl_notice("MPD connection: %s\n", mpd_connection_get_error_message(conn)); + mpd_conn_state = MPD_FAILURE; + return; + } - lwsl_notice("MPD connected.\n"); - mpd_conn_state = MPD_CONNECTED; - break; + lwsl_notice("MPD connected.\n"); + mpd_conn_state = MPD_CONNECTED; + break; - case MPD_FAILURE: - lwsl_notice("MPD connection failed.\n"); + case MPD_FAILURE: + lwsl_notice("MPD connection failed.\n"); - if(conn != NULL) - mpd_connection_free(conn); - conn = NULL; - mpd_conn_state = MPD_DISCONNECTED; - break; + if(conn != NULL) + mpd_connection_free(conn); + conn = NULL; + mpd_conn_state = MPD_DISCONNECTED; + break; - case MPD_CONNECTED: - /* Nothing to do */ - break; - } + case MPD_CONNECTED: + /* Nothing to do */ + break; + } } 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); - if(str == NULL) - str = (char *)mpd_song_get_uri(song); + str = (char *)mpd_song_get_tag(song, MPD_TAG_TITLE, 0); + if(str == NULL) + str = (char *)mpd_song_get_uri(song); - if(str == NULL) - return NULL; + if(str == NULL) + return NULL; - ptr = str; - while(*ptr++ != '\0') - if(*ptr=='"') - *ptr='\''; + ptr = str; + while(*ptr++ != '\0') + if(*ptr=='"') + *ptr='\''; - return basename(str); + return basename(str); } int mpd_put_state(char *buffer) { - struct mpd_status *status; - int len; + struct mpd_status *status; + int len; - status = mpd_run_status(conn); - if (!status) { - lwsl_err("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(conn)); - mpd_conn_state = MPD_FAILURE; - return 0; - } + status = mpd_run_status(conn); + if (!status) { + lwsl_err("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(conn)); + mpd_conn_state = MPD_FAILURE; + return 0; + } - len = snprintf(buffer, MAX_SIZE, - "{\"type\":\"state\", \"data\":{" - " \"state\":%d, \"volume\":%d, \"repeat\":%d," - " \"single\":%d, \"consume\":%d, \"random\":%d, " - " \"songpos\": %d, \"elapsedTime\": %d, \"totalTime\":%d, " - " \"currentsongid\": %d" - "}}", - mpd_status_get_state(status), - mpd_status_get_volume(status), - mpd_status_get_repeat(status), - mpd_status_get_single(status), - mpd_status_get_consume(status), - mpd_status_get_random(status), - mpd_status_get_song_pos(status), - mpd_status_get_elapsed_time(status), - mpd_status_get_total_time(status), - mpd_status_get_song_id(status)); + len = snprintf(buffer, MAX_SIZE, + "{\"type\":\"state\", \"data\":{" + " \"state\":%d, \"volume\":%d, \"repeat\":%d," + " \"single\":%d, \"consume\":%d, \"random\":%d, " + " \"songpos\": %d, \"elapsedTime\": %d, \"totalTime\":%d, " + " \"currentsongid\": %d" + "}}", + mpd_status_get_state(status), + mpd_status_get_volume(status), + mpd_status_get_repeat(status), + mpd_status_get_single(status), + mpd_status_get_consume(status), + mpd_status_get_random(status), + mpd_status_get_song_pos(status), + mpd_status_get_elapsed_time(status), + mpd_status_get_total_time(status), + mpd_status_get_song_id(status)); - queue_version = mpd_status_get_queue_version(status); - mpd_status_free(status); - return len; + queue_version = mpd_status_get_queue_version(status); + mpd_status_free(status); + return len; } int mpd_put_current_song(char *buffer) { - struct mpd_song *song; + struct mpd_song *song; int len; - song = mpd_run_current_song(conn); - if(song == NULL) - return 0; + song = mpd_run_current_song(conn); + if(song == NULL) + return 0; - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"current_song\", \"data\":" - "{\"pos\":%d, \"title\":\"%s\", \"artist\":\"%s\", \"album\":\"%s\"}}", - mpd_song_get_pos(song), - mpd_get_title(song), - mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), - mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) - ); - mpd_song_free(song); - mpd_response_finish(conn); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"current_song\", \"data\":" + "{\"pos\":%d, \"title\":\"%s\", \"artist\":\"%s\", \"album\":\"%s\"}}", + mpd_song_get_pos(song), + mpd_get_title(song), + mpd_song_get_tag(song, MPD_TAG_ARTIST, 0), + mpd_song_get_tag(song, MPD_TAG_ALBUM, 0) + ); + mpd_song_free(song); + mpd_response_finish(conn); - return len; + return len; } int mpd_put_playlist(char *buffer) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; - struct mpd_entity *entity; + char *cur = buffer; + const char *end = buffer + MAX_SIZE; + struct mpd_entity *entity; - if (!mpd_send_list_queue_meta(conn)) { - lwsl_err("MPD mpd_send_list_queue_meta: %s\n", mpd_connection_get_error_message(conn)); - mpd_conn_state = MPD_FAILURE; - return 0; - } + if (!mpd_send_list_queue_meta(conn)) { + lwsl_err("MPD mpd_send_list_queue_meta: %s\n", mpd_connection_get_error_message(conn)); + mpd_conn_state = MPD_FAILURE; + 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; - if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { - song = mpd_entity_get_song(entity); - cur += snprintf(cur, end - cur, - "{\"id\":%d, \"pos\":%d, \"duration\":%d, \"title\":\"%s\"},", - mpd_song_get_id(song), - mpd_song_get_pos(song), - mpd_song_get_duration(song), - mpd_get_title(song) - ); - } - mpd_entity_free(entity); - } + if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { + song = mpd_entity_get_song(entity); + cur += snprintf(cur, end - cur, + "{\"id\":%d, \"pos\":%d, \"duration\":%d, \"title\":\"%s\"},", + mpd_song_get_id(song), + mpd_song_get_pos(song), + mpd_song_get_duration(song), + mpd_get_title(song) + ); + } + mpd_entity_free(entity); + } - /* remove last ',' */ - cur--; - cur += snprintf(cur, end - cur, "] }"); - return cur - buffer; + /* remove last ',' */ + cur--; + cur += snprintf(cur, end - cur, "] }"); + return cur - buffer; } int mpd_put_browse(char *buffer, char *path) { - char *cur = buffer; - const char *end = buffer + MAX_SIZE; - struct mpd_entity *entity; - - if (!mpd_send_list_meta(conn, path)) { - lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn)); - mpd_conn_state = MPD_FAILURE; - return 0; - } - cur += snprintf(cur, end - cur, "{\"type\":\"browse\",\"data\":[ "); + char *cur = buffer; + const char *end = buffer + MAX_SIZE; + struct mpd_entity *entity; - while((entity = mpd_recv_entity(conn)) != NULL) { + if (!mpd_send_list_meta(conn, path)) { + lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn)); + mpd_conn_state = MPD_FAILURE; + return 0; + } + cur += snprintf(cur, end - cur, "{\"type\":\"browse\",\"data\":[ "); + + while((entity = mpd_recv_entity(conn)) != NULL) { const struct mpd_song *song; const struct mpd_directory *dir; const struct mpd_playlist *pl; switch (mpd_entity_get_type(entity)) { - case MPD_ENTITY_TYPE_UNKNOWN: - break; + case MPD_ENTITY_TYPE_UNKNOWN: + break; - case MPD_ENTITY_TYPE_SONG: - song = mpd_entity_get_song(entity); - cur += snprintf(cur, end - cur, - "{\"type\":\"song\",\"uri\":\"%s\",\"duration\":%d,\"title\":\"%s\"},", - mpd_song_get_uri(song), - mpd_song_get_duration(song), - mpd_get_title(song) - ); - break; + case MPD_ENTITY_TYPE_SONG: + song = mpd_entity_get_song(entity); + cur += snprintf(cur, end - cur, + "{\"type\":\"song\",\"uri\":\"%s\",\"duration\":%d,\"title\":\"%s\"},", + mpd_song_get_uri(song), + mpd_song_get_duration(song), + mpd_get_title(song) + ); + break; - case MPD_ENTITY_TYPE_DIRECTORY: - dir = mpd_entity_get_directory(entity); - cur += snprintf(cur, end - cur, - "{\"type\":\"directory\",\"dir\":\"%s\"},", - mpd_directory_get_path(dir) - ); - break; + case MPD_ENTITY_TYPE_DIRECTORY: + dir = mpd_entity_get_directory(entity); + cur += snprintf(cur, end - cur, + "{\"type\":\"directory\",\"dir\":\"%s\"},", + mpd_directory_get_path(dir) + ); + break; - case MPD_ENTITY_TYPE_PLAYLIST: - pl = mpd_entity_get_playlist(entity); - cur += snprintf(cur, end - cur, - "{\"type\":\"playlist\",\"plist\":\"%s\"},", - mpd_playlist_get_path(pl) - ); - break; + case MPD_ENTITY_TYPE_PLAYLIST: + pl = mpd_entity_get_playlist(entity); + cur += snprintf(cur, end - cur, + "{\"type\":\"playlist\",\"plist\":\"%s\"},", + mpd_playlist_get_path(pl) + ); + break; } - mpd_entity_free(entity); - } + mpd_entity_free(entity); + } 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)); - mpd_conn_state = MPD_FAILURE; + lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn)); + mpd_conn_state = MPD_FAILURE; return 0; } - /* remove last ',' */ - cur--; - cur += snprintf(cur, end - cur, "] }"); - return cur - buffer; + /* remove last ',' */ + cur--; + cur += snprintf(cur, end - cur, "] }"); + return cur - buffer; } diff --git a/src/mpd_client.h b/src/mpd_client.h index 760ee8f..019f17b 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -5,21 +5,6 @@ #define DO_SEND_TRACK_INFO (1 << 2) #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_PLAYLIST "MPD_API_GET_PLAYLIST" #define MPD_API_GET_TRACK_INFO "MPD_API_GET_TRACK_INFO" @@ -41,15 +26,24 @@ enum mpd_conn_states { #define MPD_API_TOGGLE_SINGLE "MPD_API_TOGGLE_SINGLE" #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, - struct libwebsocket *wsi, - enum libwebsocket_callback_reasons reason, - void *user, void *in, size_t len); - + struct libwebsocket *wsi, + enum libwebsocket_callback_reasons reason, + void *user, void *in, size_t len); void mpd_loop(); int mpd_put_state(char *buffer); int mpd_put_current_song(char *buffer); int mpd_put_playlist(char *buffer); -int mpd_put_browse(char *buffer, char *path); \ No newline at end of file +int mpd_put_browse(char *buffer, char *path); diff --git a/src/ympd.c b/src/ympd.c index a6b7f52..943fb55 100644 --- a/src/ympd.c +++ b/src/ympd.c @@ -2,109 +2,141 @@ #include #include #include +#include #include #include "http_server.h" #include "mpd_client.h" -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 */ - }, - { - "ympd-client", - callback_ympd, - sizeof(struct per_session_data__ympd), - 255, - }, +extern char *optarg; +extern char *resource_path; - { 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; void bye() { - force_exit = 1; + force_exit = 1; } int main(int argc, char **argv) { - int n = 0; - struct libwebsocket_context *context; - int opts = 0; - char interface_name[128] = ""; - const char *iface = NULL; - struct lws_context_creation_info info; - unsigned int oldus = 0; - protocol_array = protocols; + int n, gid = -1, uid = -1; + struct libwebsocket_context *context; + const char *cert_filepath = NULL; + const char *private_key_filepath = NULL; + const char *iface = NULL; + struct lws_context_creation_info info; + unsigned int oldus = 0; - atexit(bye); - memset(&info, 0, sizeof info); - info.port = 7681; + atexit(bye); + memset(&info, 0, sizeof info); + info.port = 80; - while (n >= 0) { - n = getopt(argc, argv, "i:p:"); - if (n < 0) - continue; - switch (n) { - case 'p': - info.port = atoi(optarg); - break; - case 'i': - strncpy(interface_name, optarg, sizeof interface_name); - interface_name[(sizeof interface_name) - 1] = '\0'; - iface = interface_name; - break; - case 'h': - fprintf(stderr, "Usage: %s [OPTION]...\n" - "[-=

] [--mpd-port=

] " - "[-i ] " - "[--resource_path ]\n", argv[0]); - exit(1); - } - } + while((n = getopt(argc, argv, "i:p:r:c:k:g:u:h")) != -1) { + switch (n) { + case 'i': + iface = optarg; + break; + case 'p': + info.port = atoi(optarg); + break; + case 'r': + resource_path = optarg; + break; + case 'c': + cert_filepath = optarg; + break; + case 'k': + private_key_filepath = optarg; + break; + case 'g': + gid = atoi(optarg); + break; + case 'u': + uid = atoi(optarg); + break; + case '?': + case 'h': + lwsl_err("Usage: %s [OPTION]...\n" + "\t[-p ]\n" + "\t[-i ]\n" + "\t[-r ]\n" + "\t[-c ]\n" + "\t[-k ]\n" + "\t[-g ]\n" + "\t[-u ]\n" + "\t[-h]\n" + , argv[0]); + return EXIT_FAILURE; + } + } - info.iface = iface; - info.protocols = protocols; - info.extensions = libwebsocket_get_internal_extensions(); - info.gid = -1; - info.uid = -1; - info.ssl_cert_filepath = NULL; - info.ssl_private_key_filepath = NULL; - info.options = opts; + if(cert_filepath != NULL && private_key_filepath == NULL) { + lwsl_err("private key filepath needed\n"); + return EXIT_FAILURE; + } - context = libwebsocket_create_context(&info); - if (context == NULL) { - lwsl_err("libwebsocket init failed\n"); - return EXIT_FAILURE; - } + if(private_key_filepath != NULL && cert_filepath == NULL) { + lwsl_err("public cert filepath needed\n"); + return EXIT_FAILURE; + } - n = 0; - while (n >= 0 && !force_exit) { - struct timeval tv; + info.ssl_cert_filepath = cert_filepath; + info.ssl_private_key_filepath = private_key_filepath; + 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; + } - /* - * 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) - */ + n = 0; + while (n >= 0 && !force_exit) { + struct timeval tv; - if (((unsigned int)tv.tv_usec - oldus) > 1000 * 500) { - mpd_loop(); - libwebsocket_callback_on_writable_all_protocol(&protocols[1]); - oldus = tv.tv_usec; - } + gettimeofday(&tv, NULL); - 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); - return 0; + if (((unsigned int)tv.tv_usec - oldus) > 1000 * 500) { + 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; }