mirror of
https://github.com/SuperBFG7/ympd
synced 2025-07-05 19:32:52 +00:00
Feat: initial working code
This commit is contained in:
parent
c7f5357f93
commit
97e4fe2e94
@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
project (mympd C)
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "4")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "7")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "2")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "5")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "0")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "0")
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES RELEASE)
|
||||
set(ASSETS_PATH "/usr/share/${PROJECT_NAME}/htdocs")
|
||||
@ -21,7 +21,7 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_I
|
||||
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D MG_ENABLE_SSL -D MG_ENABLE_IPV6 -D MG_DISABLE_MQTT -D MG_DISABLE_MQTT_BROKER -D MG_DISABLE_DNS_SERVER -D MG_DISABLE_COAP -D MG_DISABLE_HTTP_CGI -D MG_DISABLE_HTTP_SSI -D MG_DISABLE_HTTP_WEBDAV")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D MG_ENABLE_SSL -D MG_ENABLE_THREADS -D MG_ENABLE_IPV6 -D MG_DISABLE_MQTT -D MG_DISABLE_MQTT_BROKER -D MG_DISABLE_DNS_SERVER -D MG_DISABLE_COAP -D MG_DISABLE_HTTP_CGI -D MG_DISABLE_HTTP_SSI -D MG_DISABLE_HTTP_WEBDAV")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan")
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
@ -31,8 +31,10 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_SSL)
|
||||
set(SOURCES
|
||||
src/mympd.c
|
||||
src/mpd_client.c
|
||||
src/web_server.c
|
||||
src/list.c
|
||||
src/validate.c
|
||||
src/tiny_queue.c
|
||||
src/common.c
|
||||
dist/src/mongoose/mongoose.c
|
||||
dist/src/frozen/frozen.c
|
||||
dist/src/inih/ini.c
|
||||
|
2
PKGBUILD
2
PKGBUILD
@ -4,7 +4,7 @@
|
||||
|
||||
pkgname=mympd
|
||||
_pkgname=myMPD
|
||||
pkgver=4.7.2
|
||||
pkgver=5.0.0
|
||||
pkgrel=1
|
||||
pkgdesc="myMPD is a standalone and mobile friendly web mpdclient."
|
||||
arch=('x86_64' 'armv7h' 'aarch64')
|
||||
|
@ -4,7 +4,7 @@
|
||||
# (c) 2018 Juergen Mang <mail@jcgames.de>
|
||||
|
||||
Name: myMPD
|
||||
Version: 4.7.2
|
||||
Version: 5.0.0
|
||||
Release: 0
|
||||
License: GPL-2.0
|
||||
Group: Productivity/Multimedia/Sound/Players
|
||||
|
4
debian/changelog
vendored
4
debian/changelog
vendored
@ -1,5 +1,5 @@
|
||||
mympd (4.7.2-1) stable; urgency=medium
|
||||
mympd (5.0.0-1) stable; urgency=medium
|
||||
|
||||
* Release from master
|
||||
|
||||
-- Juergen Mang <mail@jcgames.de> Fri, 07 Dec 2018 17:12:12 +0000
|
||||
-- Juergen Mang <mail@jcgames.de> Fri, 04 Jan 2019 09:01:07 +0000
|
||||
|
@ -1,4 +1,4 @@
|
||||
var CACHE = 'myMPD-cache-v4.7.2';
|
||||
var CACHE = 'myMPD-cache-v5.0.0';
|
||||
var urlsToCache = [
|
||||
'/',
|
||||
'/player.html',
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* myMPD
|
||||
(c) 2018 Juergen Mang <mail@jcgames.de>
|
||||
(c) 2018-2019 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/mympd
|
||||
|
||||
myMPD ist fork of:
|
||||
@ -26,7 +26,7 @@
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include "validate.h"
|
||||
#include "common.h"
|
||||
|
||||
void sanitize_string(const char *data) {
|
||||
static char ok_chars[] = "abcdefghijklmnopqrstuvwxyz"
|
||||
@ -38,18 +38,11 @@ void sanitize_string(const char *data) {
|
||||
*cp = '_';
|
||||
}
|
||||
|
||||
int validate_path(char *path, const char *basepath) {
|
||||
char *rpath = NULL;
|
||||
char *ptr;
|
||||
ptr = realpath(path, rpath);
|
||||
if (ptr == NULL)
|
||||
return 1;
|
||||
if (strncmp(basepath, ptr, strlen(basepath)) == 0) {
|
||||
free(rpath);
|
||||
int copy_string(char * const dest, char const * const src, size_t const dst_len, size_t const src_len) {
|
||||
if (dst_len == 0 || src_len == 0)
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
free(rpath);
|
||||
return 1;
|
||||
}
|
||||
size_t const max = (src_len < dst_len) ? src_len : dst_len -1;
|
||||
memcpy(dest, src, max);
|
||||
dest[max] = '\0';
|
||||
return max;
|
||||
}
|
72
src/common.h
Normal file
72
src/common.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* myMPD
|
||||
(c) 2018-2019 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/mympd
|
||||
|
||||
myMPD ist fork of:
|
||||
|
||||
ympd
|
||||
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
|
||||
This project's homepage is: http://www.ympd.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __COMMON_H__
|
||||
#define __COMMON_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_SIZE 2048 * 400
|
||||
#define MAX_ELEMENTS_PER_PAGE 400
|
||||
|
||||
#define LOG_INFO() if (config.loglevel >= 1)
|
||||
#define LOG_VERBOSE() if (config.loglevel >= 2)
|
||||
#define LOG_DEBUG() if (config.loglevel == 3)
|
||||
|
||||
typedef struct {
|
||||
long mpdport;
|
||||
const char *mpdhost;
|
||||
const char *mpdpass;
|
||||
const char *webport;
|
||||
bool ssl;
|
||||
const char *sslport;
|
||||
const char *sslcert;
|
||||
const char *sslkey;
|
||||
const char *user;
|
||||
bool coverimage;
|
||||
const char *coverimagename;
|
||||
long coverimagesize;
|
||||
bool stickers;
|
||||
bool mixramp;
|
||||
const char *taglist;
|
||||
const char *searchtaglist;
|
||||
const char *browsetaglist;
|
||||
bool smartpls;
|
||||
const char *varlibdir;
|
||||
const char *etcdir;
|
||||
unsigned long max_elements_per_page;
|
||||
bool syscmds;
|
||||
bool localplayer;
|
||||
long streamport;
|
||||
const char *streamurl;
|
||||
unsigned long last_played_count;
|
||||
long loglevel;
|
||||
} t_config;
|
||||
|
||||
t_config config;
|
||||
|
||||
void sanitize_string(const char *data);
|
||||
int copy_string(char * const dest, char const * const src, size_t const dst_len, size_t const src_len);
|
||||
#endif
|
@ -18,6 +18,9 @@
|
||||
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __LIST_H__
|
||||
#define __LIST_H__
|
||||
|
||||
struct node {
|
||||
char *data;
|
||||
long value;
|
||||
@ -43,3 +46,4 @@ int list_shuffle(struct list *l);
|
||||
int list_sort_by_value(struct list *l, bool order);
|
||||
int list_swap_item(struct node *n1, struct node *n2);
|
||||
struct node *list_node_at(const struct list * l, unsigned index);
|
||||
#endif
|
||||
|
269
src/mpd_client.c
269
src/mpd_client.c
@ -32,8 +32,9 @@
|
||||
#include <poll.h>
|
||||
#include <mpd/client.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "mpd_client.h"
|
||||
#include "validate.h"
|
||||
#include "web_server.h"
|
||||
#include "config.h"
|
||||
#include "../dist/src/frozen/frozen.h"
|
||||
|
||||
@ -49,9 +50,7 @@ static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
enum mpd_idle idle_bitmask_save = 0;
|
||||
|
||||
void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
void mympd_api(struct work_request_t *request) {
|
||||
size_t n = 0;
|
||||
char *cmd;
|
||||
unsigned int uint_buf1, uint_buf2, uint_rc;
|
||||
@ -61,49 +60,40 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
char *p_charbuf1, *p_charbuf2, *p_charbuf3, *p_charbuf4;
|
||||
char p_char[4];
|
||||
enum mpd_cmd_ids cmd_id;
|
||||
struct pollfd fds[1];
|
||||
int pollrc;
|
||||
#ifdef DEBUG
|
||||
struct timespec start, end;
|
||||
#endif
|
||||
|
||||
LOG_VERBOSE() printf("API request: %s\n", msg.p);
|
||||
LOG_VERBOSE() printf("API request: %.*s\n", request->length, request->data);
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{cmd: %Q}", &cmd);
|
||||
if (je == 1)
|
||||
je = json_scanf(request->data, request->length, "{cmd: %Q}", &cmd);
|
||||
if (je == 1) {
|
||||
cmd_id = get_cmd_id(cmd);
|
||||
else
|
||||
cmd_id = get_cmd_id("MPD_API_UNKNOWN");
|
||||
}
|
||||
else {
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Invalid API request.\"}");
|
||||
printf("Error: Invalid API request.\n");
|
||||
struct work_result_t *response = (struct work_result_t*)malloc(sizeof(struct work_result_t));
|
||||
response->conn_id = request->conn_id;
|
||||
response->length = copy_string(response->data, mpd.buf, MAX_SIZE, n);
|
||||
tiny_queue_push(web_server_queue, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd_id == -1)
|
||||
cmd_id = get_cmd_id("MPD_API_UNKNOWN");
|
||||
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Leaving mpd idle mode\n");
|
||||
if (mpd_send_noidle(mpd.conn)) {
|
||||
//save idle events (processing later)
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Checking for idle events\n");
|
||||
fds[0].fd = mpd_connection_get_fd(mpd.conn);
|
||||
fds[0].events = POLLIN;
|
||||
pollrc = poll(fds, 1, 200);
|
||||
if (pollrc > 0) {
|
||||
idle_bitmask_save = mpd_recv_idle(mpd.conn, false);
|
||||
if (idle_bitmask_save > 0)
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Idle event before request: %d\n", idle_bitmask_save);
|
||||
}
|
||||
}
|
||||
mpd_response_finish(mpd.conn);
|
||||
//handle request
|
||||
#ifdef DEBUG
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
|
||||
#endif
|
||||
switch(cmd_id) {
|
||||
case MPD_API_UNKNOWN:
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}");
|
||||
printf("Unknown API request: %s\n", msg.p);
|
||||
printf("Unknown API request: %.*s\n", request->length, request->data);
|
||||
break;
|
||||
case MPD_API_LIKE:
|
||||
if (config.stickers) {
|
||||
je = json_scanf(msg.p, msg.len, "{data: {uri: %Q, like: %d}}", &p_charbuf1, &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {uri: %Q, like: %d}}", &p_charbuf1, &uint_buf1);
|
||||
if (je == 2) {
|
||||
if (!mympd_like_song_uri(p_charbuf1, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}");
|
||||
@ -118,10 +108,10 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_COLS_SAVE:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {table: %Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {table: %Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
char column_list[800];
|
||||
snprintf(column_list, 800, "%.*s", msg.len, msg.p);
|
||||
snprintf(column_list, 800, "%.*s", request->length, request->data);
|
||||
char *cols = strchr(column_list, '[');
|
||||
int len = strlen(cols);
|
||||
if (len > 1)
|
||||
@ -169,7 +159,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
break;
|
||||
case MPD_API_SYSCMD:
|
||||
if (config.syscmds == true) {
|
||||
je = json_scanf(msg.p, msg.len, "{data: {cmd: %Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {cmd: %Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
int_buf1 = list_get_value(&syscmds, p_charbuf1);
|
||||
if (int_buf1 > -1)
|
||||
@ -188,73 +178,73 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
n = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length);
|
||||
break;
|
||||
case MPD_API_SETTINGS_SET:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {notificationWeb: %B}}", &mympd_state.notificationWeb);
|
||||
je = json_scanf(request->data, request->length, "{data: {notificationWeb: %B}}", &mympd_state.notificationWeb);
|
||||
if (je == 1)
|
||||
if (!mympd_state_set("notificationWeb", (mympd_state.notificationWeb == true ? "true" : "false")))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationWeb.\"}");
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {notificationPage: %B}}", &mympd_state.notificationPage);
|
||||
je = json_scanf(request->data, request->length, "{data: {notificationPage: %B}}", &mympd_state.notificationPage);
|
||||
if (je == 1)
|
||||
if (!mympd_state_set("notificationPage", (mympd_state.notificationPage == true ? "true" : "false")))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationPage.\"}");
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {jukeboxMode: %d}}", &mympd_state.jukeboxMode);
|
||||
je = json_scanf(request->data, request->length, "{data: {jukeboxMode: %d}}", &mympd_state.jukeboxMode);
|
||||
if (je == 1) {
|
||||
snprintf(p_char, 4, "%d", mympd_state.jukeboxMode);
|
||||
if (!mympd_state_set("jukeboxMode", p_char))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}");
|
||||
}
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {jukeboxPlaylist: %Q}}", &mympd_state.jukeboxPlaylist);
|
||||
je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &mympd_state.jukeboxPlaylist);
|
||||
if (je == 1)
|
||||
if (!mympd_state_set("jukeboxPlaylist", mympd_state.jukeboxPlaylist))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxPlaylist.\"}");
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {jukeboxQueueLength: %d}}", &mympd_state.jukeboxQueueLength);
|
||||
je = json_scanf(request->data, request->length, "{data: {jukeboxQueueLength: %d}}", &mympd_state.jukeboxQueueLength);
|
||||
if (je == 1) {
|
||||
snprintf(p_char, 4, "%d", mympd_state.jukeboxQueueLength);
|
||||
if (!mympd_state_set("jukeboxQueueLength", p_char))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}");
|
||||
}
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {random: %u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {random: %u}}", &uint_buf1);
|
||||
if (je == 1)
|
||||
if (!mpd_run_random(mpd.conn, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state random.\"}");
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {repeat: %u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {repeat: %u}}", &uint_buf1);
|
||||
if (je == 1)
|
||||
if (!mpd_run_repeat(mpd.conn, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state repeat.\"}");
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {consume: %u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {consume: %u}}", &uint_buf1);
|
||||
if (je == 1)
|
||||
if (!mpd_run_consume(mpd.conn, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state consume.\"}");
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {single: %u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {single: %u}}", &uint_buf1);
|
||||
if (je == 1)
|
||||
if (!mpd_run_single(mpd.conn, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state single.\"}");
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {crossfade: %u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {crossfade: %u}}", &uint_buf1);
|
||||
if (je == 1)
|
||||
if (!mpd_run_crossfade(mpd.conn, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state crossfade.\"}");
|
||||
|
||||
if (config.mixramp) {
|
||||
je = json_scanf(msg.p, msg.len, "{data: {mixrampdb: %f}}", &float_buf);
|
||||
je = json_scanf(request->data, request->length, "{data: {mixrampdb: %f}}", &float_buf);
|
||||
if (je == 1)
|
||||
if (!mpd_run_mixrampdb(mpd.conn, float_buf))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdb.\"}");
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {mixrampdelay: %f}}", &float_buf);
|
||||
je = json_scanf(request->data, request->length, "{data: {mixrampdelay: %f}}", &float_buf);
|
||||
if (je == 1)
|
||||
if (!mpd_run_mixrampdelay(mpd.conn, float_buf))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdelay.\"}");
|
||||
}
|
||||
|
||||
je = json_scanf(msg.p, msg.len, "{data: {replaygain: %Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {replaygain: %Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
if (!mpd_send_command(mpd.conn, "replay_gain_mode", p_charbuf1, NULL))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state replaygain.\"}");
|
||||
@ -287,11 +277,11 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Smart Playlists update failed\"}");
|
||||
break;
|
||||
case MPD_API_SMARTPLS_SAVE:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {type: %Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {type: %Q}}", &p_charbuf1);
|
||||
n = 1;
|
||||
if (je == 1) {
|
||||
if (strcmp(p_charbuf1, "sticker") == 0) {
|
||||
je = json_scanf(msg.p, msg.len, "{data: {playlist: %Q, sticker: %Q, maxentries: %d}}", &p_charbuf2, &p_charbuf3, &int_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {playlist: %Q, sticker: %Q, maxentries: %d}}", &p_charbuf2, &p_charbuf3, &int_buf1);
|
||||
if (je == 3) {
|
||||
n = mympd_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, NULL, int_buf1, 0);
|
||||
free(p_charbuf2);
|
||||
@ -299,14 +289,14 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
}
|
||||
else if (strcmp(p_charbuf1, "newest") == 0) {
|
||||
je = json_scanf(msg.p, msg.len, "{data: {playlist: %Q, timerange: %d}}", &p_charbuf2, &int_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {playlist: %Q, timerange: %d}}", &p_charbuf2, &int_buf1);
|
||||
if (je == 2) {
|
||||
n = mympd_smartpls_save(p_charbuf1, p_charbuf2, NULL, NULL, 0, int_buf1);
|
||||
free(p_charbuf2);
|
||||
}
|
||||
}
|
||||
else if (strcmp(p_charbuf1, "search") == 0) {
|
||||
je = json_scanf(msg.p, msg.len, "{data: {playlist: %Q, tag: %Q, searchstr: %Q}}", &p_charbuf2, &p_charbuf3, &p_charbuf4);
|
||||
je = json_scanf(request->data, request->length, "{data: {playlist: %Q, tag: %Q, searchstr: %Q}}", &p_charbuf2, &p_charbuf3, &p_charbuf4);
|
||||
if (je == 3) {
|
||||
n = mympd_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, p_charbuf4, 0, 0);
|
||||
free(p_charbuf2);
|
||||
@ -322,7 +312,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving playlist failed\"}");
|
||||
break;
|
||||
case MPD_API_SMARTPLS_GET:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {playlist: %Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {playlist: %Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
n = mympd_smartpls_put(mpd.buf, p_charbuf1);
|
||||
free(p_charbuf1);
|
||||
@ -382,7 +372,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
n = mympd_queue_crop(mpd.buf);
|
||||
break;
|
||||
case MPD_API_QUEUE_RM_TRACK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {track:%u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {track:%u}}", &uint_buf1);
|
||||
if (je == 1) {
|
||||
if (mpd_run_delete_id(mpd.conn, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -393,7 +383,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_RM_RANGE:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {start: %u, end: %u}}", &uint_buf1, &uint_buf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {start: %u, end: %u}}", &uint_buf1, &uint_buf2);
|
||||
if (je == 2) {
|
||||
if (mpd_run_delete_range(mpd.conn, uint_buf1, uint_buf2))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -404,7 +394,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_MOVE_TRACK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {from: %u, to: %u}}", &uint_buf1, &uint_buf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {from: %u, to: %u}}", &uint_buf1, &uint_buf2);
|
||||
if (je == 2) {
|
||||
uint_buf1--;
|
||||
uint_buf2--;
|
||||
@ -419,7 +409,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYLIST_MOVE_TRACK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {plist: %Q, from: %u, to: %u }}", &p_charbuf1, &uint_buf1, &uint_buf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {plist: %Q, from: %u, to: %u }}", &p_charbuf1, &uint_buf1, &uint_buf2);
|
||||
if (je == 3) {
|
||||
uint_buf1--;
|
||||
uint_buf2--;
|
||||
@ -437,7 +427,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYER_PLAY_TRACK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: { track:%u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: { track:%u}}", &uint_buf1);
|
||||
if (je == 1) {
|
||||
if (mpd_run_play_id(mpd.conn, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -451,7 +441,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
n = mympd_put_outputs(mpd.buf);
|
||||
break;
|
||||
case MPD_API_PLAYER_TOGGLE_OUTPUT:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {output: %u, state: %u}}", &uint_buf1, &uint_buf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {output: %u, state: %u}}", &uint_buf1, &uint_buf2);
|
||||
if (je == 2) {
|
||||
if (uint_buf2) {
|
||||
if (mpd_run_enable_output(mpd.conn, uint_buf1))
|
||||
@ -472,7 +462,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYER_VOLUME_SET:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {volume:%u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {volume:%u}}", &uint_buf1);
|
||||
if (je == 1) {
|
||||
if (mpd_run_set_volume(mpd.conn, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -486,7 +476,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
n = mympd_put_volume(mpd.buf);
|
||||
break;
|
||||
case MPD_API_PLAYER_SEEK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {songid: %u, seek: %u}}", &uint_buf1, &uint_buf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {songid: %u, seek: %u}}", &uint_buf1, &uint_buf2);
|
||||
if (je == 2) {
|
||||
if (mpd_run_seek_id(mpd.conn, uint_buf1, uint_buf2))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -497,13 +487,13 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_LIST:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {offset: %u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1);
|
||||
if (je == 1) {
|
||||
n = mympd_put_queue(mpd.buf, uint_buf1, &mpd.queue_version, &mpd.queue_length);
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_LAST_PLAYED:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {offset: %u}}", &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1);
|
||||
if (je == 1) {
|
||||
n = mympd_put_last_played_songs(mpd.buf, uint_buf1);
|
||||
}
|
||||
@ -512,14 +502,14 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
n = mympd_put_current_song(mpd.buf);
|
||||
break;
|
||||
case MPD_API_DATABASE_SONGDETAILS:
|
||||
je = json_scanf(msg.p, msg.len, "{data: { uri: %Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: { uri: %Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
n = mympd_put_songdetails(mpd.buf, p_charbuf1);
|
||||
free(p_charbuf1);
|
||||
}
|
||||
break;
|
||||
case MPD_API_DATABASE_TAG_LIST:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {offset: %u, filter: %Q, tag: %Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {offset: %u, filter: %Q, tag: %Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2);
|
||||
if (je == 3) {
|
||||
n = mympd_put_db_tag(mpd.buf, uint_buf1, p_charbuf2, "", "", p_charbuf1);
|
||||
free(p_charbuf1);
|
||||
@ -527,7 +517,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_DATABASE_TAG_ALBUM_LIST:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {offset: %u, filter: %Q, search: %Q, tag: %Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2, &p_charbuf3);
|
||||
je = json_scanf(request->data, request->length, "{data: {offset: %u, filter: %Q, search: %Q, tag: %Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2, &p_charbuf3);
|
||||
if (je == 4) {
|
||||
n = mympd_put_db_tag(mpd.buf, uint_buf1, "Album", p_charbuf3, p_charbuf2, p_charbuf1);
|
||||
free(p_charbuf1);
|
||||
@ -536,7 +526,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_DATABASE_TAG_ALBUM_TITLE_LIST:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {album: %Q, search: %Q, tag: %Q}}", &p_charbuf1, &p_charbuf2, &p_charbuf3);
|
||||
je = json_scanf(request->data, request->length, "{data: {album: %Q, search: %Q, tag: %Q}}", &p_charbuf1, &p_charbuf2, &p_charbuf3);
|
||||
if (je == 3) {
|
||||
n = mympd_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3);
|
||||
free(p_charbuf1);
|
||||
@ -545,7 +535,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYLIST_RENAME:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {from: %Q, to: %Q}}", &p_charbuf1, &p_charbuf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {from: %Q, to: %Q}}", &p_charbuf1, &p_charbuf2);
|
||||
if (je == 2) {
|
||||
//rename smart playlist
|
||||
char old_pl_file[400];
|
||||
@ -583,14 +573,14 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYLIST_LIST:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {offset: %u, filter: %Q}}", &uint_buf1, &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {offset: %u, filter: %Q}}", &uint_buf1, &p_charbuf1);
|
||||
if (je == 2) {
|
||||
n = mympd_put_playlists(mpd.buf, uint_buf1, p_charbuf1);
|
||||
free(p_charbuf1);
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYLIST_CONTENT_LIST:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {uri: %Q, offset:%u, filter:%Q}}", &p_charbuf1, &uint_buf1, &p_charbuf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {uri: %Q, offset:%u, filter:%Q}}", &p_charbuf1, &uint_buf1, &p_charbuf2);
|
||||
if (je == 3) {
|
||||
n = mympd_put_playlist_list(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2);
|
||||
free(p_charbuf1);
|
||||
@ -598,7 +588,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYLIST_ADD_TRACK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {plist:%Q, uri:%Q}}", &p_charbuf1, &p_charbuf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {plist:%Q, uri:%Q}}", &p_charbuf1, &p_charbuf2);
|
||||
if (je == 2) {
|
||||
if (mpd_run_playlist_add(mpd.conn, p_charbuf1, p_charbuf2))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Added %s to playlist %s\"}", p_charbuf2, p_charbuf1);
|
||||
@ -611,7 +601,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYLIST_CLEAR:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
if (mpd_run_playlist_clear(mpd.conn, p_charbuf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -623,7 +613,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYLIST_RM_TRACK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {uri:%Q, track:%u}}", &p_charbuf1, &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {uri:%Q, track:%u}}", &p_charbuf1, &uint_buf1);
|
||||
if (je == 2) {
|
||||
if (mpd_run_playlist_delete(mpd.conn, p_charbuf1, uint_buf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -635,7 +625,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_DATABASE_FILESYSTEM_LIST:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {offset:%u, filter:%Q, path:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {offset:%u, filter:%Q, path:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2);
|
||||
if (je == 3) {
|
||||
n = mympd_put_browse(mpd.buf, p_charbuf2, uint_buf1, p_charbuf1);
|
||||
free(p_charbuf1);
|
||||
@ -643,7 +633,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_ADD_TRACK_AFTER:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {uri:%Q, to:%d}}", &p_charbuf1, &int_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {uri:%Q, to:%d}}", &p_charbuf1, &int_buf1);
|
||||
if (je == 2) {
|
||||
int_rc = mpd_run_add_id_to(mpd.conn, p_charbuf1, int_buf1);
|
||||
if (int_rc > -1 )
|
||||
@ -656,7 +646,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_REPLACE_TRACK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {uri:%Q }}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {uri:%Q }}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
if (!mpd_run_clear(mpd.conn)) {
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}");
|
||||
@ -676,7 +666,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_ADD_TRACK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
if (mpd_run_add(mpd.conn, p_charbuf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -688,7 +678,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_ADD_PLAY_TRACK:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
int_buf1 = mpd_run_add_id(mpd.conn, p_charbuf1);
|
||||
if (int_buf1 != -1) {
|
||||
@ -707,7 +697,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_REPLACE_PLAYLIST:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {plist:%Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {plist:%Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
if (!mpd_run_clear(mpd.conn)) {
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}");
|
||||
@ -727,7 +717,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_ADD_PLAYLIST:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {plist:%Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {plist:%Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
if (mpd_run_load(mpd.conn, p_charbuf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -739,7 +729,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_SAVE:
|
||||
je = json_scanf(msg.p, msg.len, "{ data: {plist:%Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{ data: {plist:%Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
if (mpd_run_save(mpd.conn, p_charbuf1))
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}");
|
||||
@ -751,7 +741,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_QUEUE_SEARCH:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {offset:%u, filter:%Q, searchstr:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2);
|
||||
je = json_scanf(request->data, request->length, "{data: {offset:%u, filter:%Q, searchstr:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2);
|
||||
if (je == 3) {
|
||||
n = mympd_search_queue(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2);
|
||||
free(p_charbuf1);
|
||||
@ -759,7 +749,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_DATABASE_SEARCH:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {searchstr:%Q, filter:%Q, plist:%Q, offset:%u}}", &p_charbuf1, &p_charbuf2, &p_charbuf3, &uint_buf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {searchstr:%Q, filter:%Q, plist:%Q, offset:%u}}", &p_charbuf1, &p_charbuf2, &p_charbuf3, &uint_buf1);
|
||||
if (je == 4) {
|
||||
n = mympd_search(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1);
|
||||
free(p_charbuf1);
|
||||
@ -767,7 +757,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_DATABASE_SEARCH_ADV:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {expression:%Q, sort:%Q, sortdesc:%B, plist:%Q, offset:%u}}",
|
||||
je = json_scanf(request->data, request->length, "{data: {expression:%Q, sort:%Q, sortdesc:%B, plist:%Q, offset:%u}}",
|
||||
&p_charbuf1, &p_charbuf2, &bool_buf, &p_charbuf3, &uint_buf1);
|
||||
if (je == 5) {
|
||||
n = mympd_search_adv(mpd.buf, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1);
|
||||
@ -785,7 +775,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
}
|
||||
break;
|
||||
case MPD_API_PLAYLIST_RM:
|
||||
je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1);
|
||||
je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1);
|
||||
if (je == 1) {
|
||||
//remove smart playlist
|
||||
char pl_file[400];
|
||||
@ -833,33 +823,31 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) {
|
||||
fprintf(stderr, "DEBUG: Time used: %lu\n", delta_us);
|
||||
#endif
|
||||
|
||||
if (n == 0)
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd %s\"}", cmd);
|
||||
if (n == 0) {
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd %s.\"}", cmd);
|
||||
}
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Send http response to connection %lu (first 800 chars):\n%*.*s\n", request->conn_id, 0, 800, mpd.buf);
|
||||
|
||||
struct work_result_t *response = (struct work_result_t*)malloc(sizeof(struct work_result_t));
|
||||
response->conn_id = request->conn_id;
|
||||
response->length = copy_string(response->data, mpd.buf, MAX_SIZE, n);
|
||||
tiny_queue_push(web_server_queue, response);
|
||||
|
||||
if (is_websocket(nc)) {
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Send websocket response:\n %s\n", mpd.buf);
|
||||
mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, mpd.buf, n);
|
||||
}
|
||||
else {
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Send http response (first 800 chars):\n%*.*s\n", 0, 800, mpd.buf);
|
||||
mg_send_http_chunk(nc, mpd.buf, n);
|
||||
}
|
||||
free(cmd);
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Entering mpd idle mode\n");
|
||||
mpd_send_idle(mpd.conn);
|
||||
free(request);
|
||||
}
|
||||
|
||||
void mympd_notify(struct mg_mgr *s) {
|
||||
for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) {
|
||||
if (!is_websocket(c))
|
||||
continue;
|
||||
mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf));
|
||||
}
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Websocket notify: %s\n", mpd.buf);
|
||||
void mympd_notify(size_t len) {
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Websocket notify: %s.\n", mpd.buf);
|
||||
|
||||
struct work_result_t *response = (struct work_result_t*)malloc(sizeof(struct work_result_t));
|
||||
response->conn_id = 0;
|
||||
response->length = copy_string(response->data, mpd.buf, MAX_SIZE, len);
|
||||
tiny_queue_push(web_server_queue, response);
|
||||
}
|
||||
|
||||
void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) {
|
||||
int len = 0;
|
||||
void mympd_parse_idle(int idle_bitmask) {
|
||||
size_t n = 0;
|
||||
for (unsigned j = 0;; j++) {
|
||||
enum mpd_idle idle_event = 1 << j;
|
||||
const char *idle_name = mpd_idle_name(idle_event);
|
||||
@ -869,19 +857,19 @@ void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) {
|
||||
LOG_VERBOSE() printf("MPD idle event: %s\n", idle_name);
|
||||
switch(idle_event) {
|
||||
case MPD_IDLE_DATABASE:
|
||||
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}");
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}");
|
||||
mympd_smartpls_update_all();
|
||||
break;
|
||||
case MPD_IDLE_STORED_PLAYLIST:
|
||||
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}");
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}");
|
||||
break;
|
||||
case MPD_IDLE_QUEUE:
|
||||
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}");
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}");
|
||||
if (mympd_state.jukeboxMode > 0)
|
||||
mympd_jukebox();
|
||||
break;
|
||||
case MPD_IDLE_PLAYER:
|
||||
len = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length);
|
||||
n = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length);
|
||||
if (mpd.song_id != mpd.last_song_id) {
|
||||
if (mpd.last_last_played_id != mpd.song_id)
|
||||
mympd_last_played_list(mpd.song_id);
|
||||
@ -893,31 +881,32 @@ void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) {
|
||||
}
|
||||
break;
|
||||
case MPD_IDLE_MIXER:
|
||||
len = mympd_put_volume(mpd.buf);
|
||||
n = mympd_put_volume(mpd.buf);
|
||||
break;
|
||||
case MPD_IDLE_OUTPUT:
|
||||
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_outputs\"}");
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_outputs\"}");
|
||||
break;
|
||||
case MPD_IDLE_OPTIONS:
|
||||
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_options\"}");
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_options\"}");
|
||||
break;
|
||||
case MPD_IDLE_UPDATE:
|
||||
len = mympd_get_updatedb_state(mpd.buf);
|
||||
n = mympd_get_updatedb_state(mpd.buf);
|
||||
break;
|
||||
case MPD_IDLE_STICKER:
|
||||
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_sticker\"}");
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_sticker\"}");
|
||||
break;
|
||||
case MPD_IDLE_SUBSCRIPTION:
|
||||
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_subscription\"}");
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_subscription\"}");
|
||||
break;
|
||||
case MPD_IDLE_MESSAGE:
|
||||
len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_message\"}");
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_message\"}");
|
||||
break;
|
||||
default:
|
||||
len = 0;
|
||||
n = 0;
|
||||
}
|
||||
if (n > 0) {
|
||||
mympd_notify(n);
|
||||
}
|
||||
if (len > 0)
|
||||
mympd_notify(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1039,15 +1028,16 @@ void mympd_mpd_features() {
|
||||
|
||||
if (LIBMPDCLIENT_CHECK_VERSION(2, 17, 0) && mpd_connection_cmp_server_version(mpd.conn, 0, 21, 0) >= 0) {
|
||||
mpd.feat_advsearch = true;
|
||||
LOG_INFO() printf("Enabling advanced search\n");
|
||||
LOG_INFO() printf("Enabling advanced search.\n");
|
||||
}
|
||||
else
|
||||
LOG_INFO() printf("Disabling advanced search, depends on mpd >= 0.21.0 and libmpdclient >= 2.17.0\n");
|
||||
LOG_INFO() printf("Disabling advanced search, depends on mpd >= 0.21.0 and libmpdclient >= 2.17.0.\n");
|
||||
}
|
||||
|
||||
void mympd_idle(struct mg_mgr *s, int timeout) {
|
||||
void mympd_idle(int timeout) {
|
||||
struct pollfd fds[1];
|
||||
int pollrc;
|
||||
size_t n = 0;
|
||||
|
||||
switch (mpd.conn_state) {
|
||||
case MPD_DISCONNECTED:
|
||||
@ -1056,24 +1046,24 @@ void mympd_idle(struct mg_mgr *s, int timeout) {
|
||||
mpd.conn = mpd_connection_new(config.mpdhost, config.mpdport, mpd.timeout);
|
||||
if (mpd.conn == NULL) {
|
||||
printf("MPD connection failed.");
|
||||
snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}");
|
||||
mympd_notify(s);
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}");
|
||||
mympd_notify(n);
|
||||
mpd.conn_state = MPD_FAILURE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) {
|
||||
printf("MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn));
|
||||
snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn));
|
||||
mympd_notify(s);
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn));
|
||||
mympd_notify(n);
|
||||
mpd.conn_state = MPD_FAILURE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.mpdpass && !mpd_run_password(mpd.conn, config.mpdpass)) {
|
||||
printf("MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn));
|
||||
snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn));
|
||||
mympd_notify(s);
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn));
|
||||
mympd_notify(n);
|
||||
mpd.conn_state = MPD_FAILURE;
|
||||
return;
|
||||
}
|
||||
@ -1090,8 +1080,8 @@ void mympd_idle(struct mg_mgr *s, int timeout) {
|
||||
|
||||
case MPD_FAILURE:
|
||||
printf("MPD connection failed.\n");
|
||||
snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}");
|
||||
mympd_notify(s);
|
||||
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}");
|
||||
mympd_notify(n);
|
||||
|
||||
case MPD_DISCONNECT:
|
||||
case MPD_RECONNECT:
|
||||
@ -1105,25 +1095,26 @@ void mympd_idle(struct mg_mgr *s, int timeout) {
|
||||
fds[0].fd = mpd_connection_get_fd(mpd.conn);
|
||||
fds[0].events = POLLIN;
|
||||
pollrc = poll(fds, 1, timeout);
|
||||
if (pollrc > 0 || idle_bitmask_save > 0) {
|
||||
//Handle idle event
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Leaving mpd idle mode\n");
|
||||
unsigned mpd_client_queue_length = tiny_queue_length(mpd_client_queue);
|
||||
if (pollrc > 0 || mpd_client_queue_length > 0) {
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Leaving mpd idle mode.\n");
|
||||
mpd_send_noidle(mpd.conn);
|
||||
if (pollrc > 0) {
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Checking for idle events\n");
|
||||
//Handle idle events
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Checking for idle events.\n");
|
||||
enum mpd_idle idle_bitmask = mpd_recv_idle(mpd.conn, false);
|
||||
mympd_parse_idle(s, idle_bitmask);
|
||||
mympd_parse_idle(idle_bitmask);
|
||||
}
|
||||
else {
|
||||
mpd_response_finish(mpd.conn);
|
||||
}
|
||||
if (idle_bitmask_save > 0) {
|
||||
//Handle idle event saved in mympd_callback
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Handle saved idle event\n");
|
||||
mympd_parse_idle(s, idle_bitmask_save);
|
||||
idle_bitmask_save = 0;
|
||||
if (mpd_client_queue_length > 0) {
|
||||
//Handle request
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Handle request.\n");
|
||||
struct work_request_t *req = tiny_queue_shift(mpd_client_queue);
|
||||
mympd_api(req);
|
||||
}
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Entering mpd idle mode\n");
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Entering mpd idle mode.\n");
|
||||
mpd_send_idle(mpd.conn);
|
||||
}
|
||||
break;
|
||||
@ -2647,7 +2638,7 @@ int mympd_put_stats(char *buffer) {
|
||||
|
||||
void mympd_disconnect() {
|
||||
mpd.conn_state = MPD_DISCONNECT;
|
||||
mympd_idle(NULL, 0);
|
||||
mympd_idle(100);
|
||||
}
|
||||
|
||||
int mympd_smartpls_put(char *buffer, char *playlist) {
|
||||
|
@ -26,7 +26,10 @@
|
||||
#define __MPD_CLIENT_H__
|
||||
|
||||
#include "../dist/src/mongoose/mongoose.h"
|
||||
#include "common.h"
|
||||
#include "web_server.h"
|
||||
#include "list.h"
|
||||
#include "tiny_queue.h"
|
||||
|
||||
#define RETURN_ERROR_AND_RECOVER(X) do { \
|
||||
printf("MPD %s: %s\n", X, mpd_connection_get_error_message(mpd.conn)); \
|
||||
@ -65,13 +68,6 @@
|
||||
} while (0)
|
||||
|
||||
|
||||
#define LOG_INFO() if (config.loglevel >= 1)
|
||||
#define LOG_VERBOSE() if (config.loglevel >= 2)
|
||||
#define LOG_DEBUG() if (config.loglevel == 3)
|
||||
|
||||
#define MAX_SIZE 2048 * 400
|
||||
#define MAX_ELEMENTS_PER_PAGE 400
|
||||
|
||||
#define GEN_ENUM(X) X,
|
||||
#define GEN_STR(X) #X,
|
||||
#define MPD_CMDS(X) \
|
||||
@ -153,7 +149,6 @@ struct t_mpd {
|
||||
|
||||
// Reponse Buffer
|
||||
char buf[MAX_SIZE];
|
||||
size_t buf_size;
|
||||
|
||||
// States
|
||||
int song_id;
|
||||
@ -181,38 +176,6 @@ struct list mympd_browsetags;
|
||||
struct list last_played;
|
||||
struct list syscmds;
|
||||
|
||||
typedef struct {
|
||||
long mpdport;
|
||||
const char *mpdhost;
|
||||
const char *mpdpass;
|
||||
const char *webport;
|
||||
bool ssl;
|
||||
const char *sslport;
|
||||
const char *sslcert;
|
||||
const char *sslkey;
|
||||
const char *user;
|
||||
bool coverimage;
|
||||
const char *coverimagename;
|
||||
long coverimagesize;
|
||||
bool stickers;
|
||||
bool mixramp;
|
||||
const char *taglist;
|
||||
const char *searchtaglist;
|
||||
const char *browsetaglist;
|
||||
bool smartpls;
|
||||
const char *varlibdir;
|
||||
const char *etcdir;
|
||||
unsigned long max_elements_per_page;
|
||||
bool syscmds;
|
||||
bool localplayer;
|
||||
long streamport;
|
||||
const char *streamurl;
|
||||
unsigned long last_played_count;
|
||||
long loglevel;
|
||||
} t_config;
|
||||
|
||||
t_config config;
|
||||
|
||||
typedef struct {
|
||||
long playCount;
|
||||
long skipCount;
|
||||
@ -236,16 +199,13 @@ typedef struct {
|
||||
} t_mympd_state;
|
||||
|
||||
t_mympd_state mympd_state;
|
||||
|
||||
static int is_websocket(const struct mg_connection *nc) {
|
||||
return nc->flags & MG_F_IS_WEBSOCKET;
|
||||
}
|
||||
tiny_queue_t *mpd_client_queue;
|
||||
|
||||
int randrange(int n);
|
||||
void mympd_idle(struct mg_mgr *sm, int timeout);
|
||||
void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask);
|
||||
void callback_mympd(struct mg_connection *nc, const struct mg_str msg);
|
||||
void mympd_notify(struct mg_mgr *s);
|
||||
void mympd_idle(int timeout);
|
||||
void mympd_parse_idle(int idle_bitmask);
|
||||
void mympd_api(struct work_request_t *request);
|
||||
void mympd_notify(size_t n);
|
||||
bool mympd_count_song_id(int song_id, char *name, int value);
|
||||
bool mympd_count_song_uri(const char *uri, char *name, int value);
|
||||
bool mympd_like_song_uri(const char *uri, int value);
|
||||
|
130
src/mympd.c
130
src/mympd.c
@ -30,97 +30,23 @@
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <libgen.h>
|
||||
#include <pthread.h>
|
||||
#include <mpd/client.h>
|
||||
|
||||
#include "../dist/src/mongoose/mongoose.h"
|
||||
#include "../dist/src/frozen/frozen.h"
|
||||
#include "../dist/src/inih/ini.h"
|
||||
#include "common.h"
|
||||
#include "mpd_client.h"
|
||||
#include "web_server.h"
|
||||
#include "config.h"
|
||||
|
||||
static sig_atomic_t s_signal_received = 0;
|
||||
static struct mg_serve_http_opts s_http_server_opts;
|
||||
|
||||
|
||||
static void signal_handler(int sig_num) {
|
||||
signal(sig_num, signal_handler); // Reinstantiate signal handler
|
||||
s_signal_received = sig_num;
|
||||
}
|
||||
|
||||
static void handle_api(struct mg_connection *nc, struct http_message *hm) {
|
||||
if (!is_websocket(nc))
|
||||
mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n");
|
||||
|
||||
char buf[1000] = {0};
|
||||
int len = sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len;
|
||||
memcpy(buf, hm->body.p, len);
|
||||
struct mg_str d = {buf, len};
|
||||
callback_mympd(nc, d);
|
||||
|
||||
if (!is_websocket(nc))
|
||||
mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */
|
||||
}
|
||||
|
||||
static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
switch(ev) {
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
LOG_VERBOSE() printf("New websocket request: %.*s\n", hm->uri.len, hm->uri.p);
|
||||
if (mg_vcmp(&hm->uri, "/ws") != 0) {
|
||||
printf("ERROR: Websocket request not to /ws, closing connection\n");
|
||||
mg_printf(nc, "%s", "HTTP/1.1 403 FORBIDDEN\r\n\r\n");
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
|
||||
LOG_VERBOSE() printf("New Websocket connection established\n");
|
||||
struct mg_str d = mg_mk_str("{\"cmd\":\"MPD_API_WELCOME\"}");
|
||||
callback_mympd(nc, d);
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
LOG_VERBOSE() printf("HTTP request: %.*s\n", hm->uri.len, hm->uri.p);
|
||||
if (mg_vcmp(&hm->uri, "/api") == 0)
|
||||
handle_api(nc, hm);
|
||||
else
|
||||
mg_serve_http(nc, hm, s_http_server_opts);
|
||||
break;
|
||||
}
|
||||
case MG_EV_CLOSE: {
|
||||
if (is_websocket(nc)) {
|
||||
LOG_VERBOSE() printf("Websocket connection closed\n");
|
||||
}
|
||||
else {
|
||||
LOG_VERBOSE() printf("HTTP connection closed\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ev_handler_http(struct mg_connection *nc_http, int ev, void *ev_data) {
|
||||
char *host;
|
||||
char host_header[1024];
|
||||
switch(ev) {
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
struct mg_str *host_hdr = mg_get_http_header(hm, "Host");
|
||||
snprintf(host_header, 1024, "%.*s", host_hdr->len, host_hdr->p);
|
||||
host = strtok(host_header, ":");
|
||||
char s_redirect[250];
|
||||
if (strcmp(config.sslport, "443") == 0)
|
||||
snprintf(s_redirect, 250, "https://%s/", host);
|
||||
else
|
||||
snprintf(s_redirect, 250, "https://%s:%s/", host, config.sslport);
|
||||
LOG_VERBOSE() printf("Redirecting to %s\n", s_redirect);
|
||||
mg_http_send_redirect(nc_http, 301, mg_mk_str(s_redirect), mg_mk_str(NULL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int inihandler(void* user, const char* section, const char* name, const char* value) {
|
||||
t_config* p_config = (t_config*)user;
|
||||
char *crap;
|
||||
@ -373,6 +299,36 @@ bool testdir(char *name, char *dirname) {
|
||||
}
|
||||
}
|
||||
|
||||
void *mpd_client_thread(void *arg) {
|
||||
struct mg_mgr *mgr = (struct mg_mgr *) arg;
|
||||
while (s_signal_received == 0) {
|
||||
mympd_idle(100);
|
||||
}
|
||||
mympd_disconnect();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *web_server_thread(void *arg) {
|
||||
struct mg_mgr *mgr = (struct mg_mgr *) arg;
|
||||
while (s_signal_received == 0) {
|
||||
mg_mgr_poll(mgr, 10);
|
||||
unsigned web_server_queue_length = tiny_queue_length(web_server_queue);
|
||||
if (web_server_queue_length > 0) {
|
||||
struct work_result_t *response = tiny_queue_shift(web_server_queue);
|
||||
if (response->conn_id == 0) {
|
||||
//Websocket notify from mpd idle
|
||||
send_ws_notify(mgr, response);
|
||||
}
|
||||
else {
|
||||
//api response
|
||||
send_api_response(mgr, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
mg_mgr_free(mgr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
struct mg_mgr mgr;
|
||||
struct mg_connection *nc;
|
||||
@ -380,6 +336,8 @@ int main(int argc, char **argv) {
|
||||
struct mg_bind_opts bind_opts;
|
||||
const char *err;
|
||||
char testdirname[400];
|
||||
mpd_client_queue = tiny_queue_create();
|
||||
web_server_queue = tiny_queue_create();
|
||||
|
||||
//defaults
|
||||
config.mpdhost = "127.0.0.1";
|
||||
@ -451,7 +409,7 @@ int main(int argc, char **argv) {
|
||||
mg_mgr_init(&mgr, NULL);
|
||||
|
||||
if (config.ssl == true) {
|
||||
nc_http = mg_bind(&mgr, config.webport, ev_handler_http);
|
||||
nc_http = mg_bind(&mgr, config.webport, ev_handler_redirect);
|
||||
if (nc_http == NULL) {
|
||||
printf("Error listening on port %s\n", config.webport);
|
||||
return EXIT_FAILURE;
|
||||
@ -552,13 +510,25 @@ int main(int argc, char **argv) {
|
||||
if (config.ssl == true)
|
||||
LOG_INFO() printf("Listening on ssl port %s\n", config.sslport);
|
||||
|
||||
pthread_t mpd_client, web_server;
|
||||
pthread_create(&mpd_client, NULL, mpd_client_thread, &mgr);
|
||||
pthread_create(&web_server, NULL, web_server_thread, &mgr);
|
||||
|
||||
//Do nothing...
|
||||
|
||||
pthread_join(mpd_client, NULL);
|
||||
pthread_join(web_server, NULL);
|
||||
|
||||
/*
|
||||
mg_start_thread(mpd_client_thread, &mgr);
|
||||
|
||||
while (s_signal_received == 0) {
|
||||
mg_mgr_poll(&mgr, 100);
|
||||
mympd_idle(&mgr, 0);
|
||||
}
|
||||
mg_mgr_free(&mgr);
|
||||
*/
|
||||
|
||||
list_free(&mpd_tags);
|
||||
list_free(&mympd_tags);
|
||||
mympd_disconnect();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
80
src/tiny_queue.c
Normal file
80
src/tiny_queue.c
Normal file
@ -0,0 +1,80 @@
|
||||
/* myMPD
|
||||
(c) 2018 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/mympd
|
||||
|
||||
This linked list implementation is based on: https://github.com/joshkunz/ashuffle
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include "tiny_queue.h"
|
||||
|
||||
tiny_queue_t* tiny_queue_create() {
|
||||
struct tiny_queue_t* queue = (struct tiny_queue_t*)malloc(sizeof(struct tiny_queue_t));
|
||||
queue->head = NULL;
|
||||
queue->tail = NULL;
|
||||
queue->length = 0;
|
||||
|
||||
queue->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
||||
queue->wakeup = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
||||
return queue;
|
||||
}
|
||||
|
||||
void tiny_queue_push(tiny_queue_t *queue, void *data) {
|
||||
pthread_mutex_lock(&queue->mutex);
|
||||
struct tiny_msg_t* new_node = (struct tiny_msg_t*)malloc(sizeof(struct tiny_msg_t));
|
||||
new_node->data = data;
|
||||
new_node->next = NULL;
|
||||
queue->length++;
|
||||
if (queue->head == NULL && queue->tail == NULL){
|
||||
queue->head = queue->tail = new_node;
|
||||
}
|
||||
else {
|
||||
queue->tail->next = new_node;
|
||||
queue->tail = new_node;
|
||||
}
|
||||
pthread_mutex_unlock(&queue->mutex);
|
||||
pthread_cond_signal(&queue->wakeup);
|
||||
}
|
||||
|
||||
int tiny_queue_length(tiny_queue_t *queue) {
|
||||
pthread_mutex_lock(&queue->mutex);
|
||||
unsigned len = queue->length;
|
||||
pthread_mutex_unlock(&queue->mutex);
|
||||
return len;
|
||||
}
|
||||
|
||||
void *tiny_queue_shift(tiny_queue_t *queue) {
|
||||
pthread_mutex_lock(&queue->mutex);
|
||||
while (queue->head == NULL) {
|
||||
// block if buffer is empty
|
||||
pthread_cond_wait(&queue->wakeup, &queue->mutex);
|
||||
}
|
||||
|
||||
struct tiny_msg_t* current_head = queue->head;
|
||||
void *data = current_head->data;
|
||||
if (queue->head == queue->tail) {
|
||||
queue->head = queue->tail = NULL;
|
||||
}
|
||||
else {
|
||||
queue->head = queue->head->next;
|
||||
}
|
||||
free(current_head);
|
||||
queue->length--;
|
||||
pthread_mutex_unlock(&queue->mutex);
|
||||
return data;
|
||||
}
|
@ -2,11 +2,7 @@
|
||||
(c) 2018 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/mympd
|
||||
|
||||
myMPD ist fork of:
|
||||
|
||||
ympd
|
||||
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
|
||||
This project's homepage is: http://www.ympd.org
|
||||
This linked list implementation is based on: https://github.com/joshkunz/ashuffle
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -21,6 +17,24 @@
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
#ifndef __TINY_QUEUE_H__
|
||||
#define __TINY_QUEUE_H__
|
||||
|
||||
void sanitize_string(const char *data);
|
||||
int validate_path(char *path, const char *basepath);
|
||||
typedef struct tiny_msg_t {
|
||||
void *data;
|
||||
struct tiny_msg_t *next;
|
||||
} tiny_msg_t;
|
||||
|
||||
typedef struct tiny_queue_t {
|
||||
unsigned length;
|
||||
struct tiny_msg_t *head;
|
||||
struct tiny_msg_t *tail;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t wakeup;
|
||||
} tiny_queue_t;
|
||||
|
||||
tiny_queue_t* tiny_queue_create();
|
||||
void tiny_queue_push(struct tiny_queue_t *queue, void *data);
|
||||
void *tiny_queue_shift(struct tiny_queue_t *queue);
|
||||
int tiny_queue_length(struct tiny_queue_t *queue);
|
||||
#endif
|
144
src/web_server.c
Normal file
144
src/web_server.c
Normal file
@ -0,0 +1,144 @@
|
||||
/* myMPD
|
||||
(c) 2018-2019 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/mympd
|
||||
|
||||
myMPD ist fork of:
|
||||
|
||||
ympd
|
||||
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
|
||||
This project's homepage is: http://www.ympd.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "web_server.h"
|
||||
#include "mpd_client.h"
|
||||
|
||||
static unsigned long s_next_id = 1;
|
||||
|
||||
int is_websocket(const struct mg_connection *nc) {
|
||||
return nc->flags & MG_F_IS_WEBSOCKET;
|
||||
}
|
||||
|
||||
void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) {
|
||||
struct mg_connection *c;
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Got ws notify, broadcasting\n");
|
||||
|
||||
for (c = mg_next(mgr, NULL); c != NULL; c = mg_next(mgr, c)) {
|
||||
if (!is_websocket(c))
|
||||
continue;
|
||||
mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, response->data, response->length);
|
||||
}
|
||||
free(response);
|
||||
}
|
||||
|
||||
void send_api_response(struct mg_mgr *mgr, struct work_result_t *response) {
|
||||
struct mg_connection *c;
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Got API response for connection %lu.\n", response->conn_id);
|
||||
|
||||
for (c = mg_next(mgr, NULL); c != NULL; c = mg_next(mgr, c)) {
|
||||
if (c->user_data != NULL) {
|
||||
if ((unsigned long)c->user_data == response->conn_id) {
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: Sending to connection %lu: %s\n", (unsigned long)c->user_data, response->data);
|
||||
mg_send_head(c, 200, response->length, "Content-Type: application/json");
|
||||
mg_printf(c, "%s", response->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(response);
|
||||
}
|
||||
|
||||
void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
|
||||
(void) nc;
|
||||
(void) ev_data;
|
||||
|
||||
switch(ev) {
|
||||
case MG_EV_ACCEPT: {
|
||||
nc->user_data = (void *)++s_next_id;
|
||||
LOG_DEBUG() fprintf(stderr, "DEBUG: New connection id %lu.\n", s_next_id);
|
||||
break;
|
||||
}
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
LOG_VERBOSE() printf("New websocket request: %.*s\n", hm->uri.len, hm->uri.p);
|
||||
if (mg_vcmp(&hm->uri, "/ws") != 0) {
|
||||
printf("ERROR: Websocket request not to /ws, closing connection\n");
|
||||
mg_printf(nc, "%s", "HTTP/1.1 403 FORBIDDEN\r\n\r\n");
|
||||
nc->flags |= MG_F_SEND_AND_CLOSE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
|
||||
LOG_VERBOSE() printf("New Websocket connection established.\n");
|
||||
char response[] = "{\"type\": \"welcome\", \"data\": {\"mympdVersion\": \"" MYMPD_VERSION "\"}}";
|
||||
mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, response, strlen(response));
|
||||
break;
|
||||
}
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
LOG_VERBOSE() printf("HTTP request: %.*s\n", hm->uri.len, hm->uri.p);
|
||||
if (mg_vcmp(&hm->uri, "/api") == 0) {
|
||||
struct work_request_t *request = (struct work_request_t*)malloc(sizeof(struct work_request_t));
|
||||
request->conn_id = (unsigned long)nc->user_data;
|
||||
request->length = copy_string(request->data, hm->body.p, 1000, hm->body.len);
|
||||
//sizeof(request->data) - 1 < hm->body.len ? sizeof(request->data) - 1 : hm->body.len;
|
||||
//memcpy(request->data, hm->body.p, request->length);
|
||||
tiny_queue_push(mpd_client_queue, request);
|
||||
}
|
||||
else {
|
||||
mg_serve_http(nc, hm, s_http_server_opts);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MG_EV_CLOSE: {
|
||||
if (nc->user_data) {
|
||||
LOG_VERBOSE() fprintf(stderr, "HTTP connection %lu closed.\n", (unsigned long)nc->user_data);
|
||||
nc->user_data = NULL;
|
||||
}
|
||||
else {
|
||||
LOG_VERBOSE() printf("HTTP connection closed.\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_data) {
|
||||
char *host;
|
||||
char host_header[1024];
|
||||
switch(ev) {
|
||||
case MG_EV_HTTP_REQUEST: {
|
||||
struct http_message *hm = (struct http_message *) ev_data;
|
||||
struct mg_str *host_hdr = mg_get_http_header(hm, "Host");
|
||||
snprintf(host_header, 1024, "%.*s", host_hdr->len, host_hdr->p);
|
||||
host = strtok(host_header, ":");
|
||||
char s_redirect[250];
|
||||
if (strcmp(config.sslport, "443") == 0)
|
||||
snprintf(s_redirect, 250, "https://%s/", host);
|
||||
else
|
||||
snprintf(s_redirect, 250, "https://%s:%s/", host, config.sslport);
|
||||
LOG_VERBOSE() printf("Redirecting to %s\n", s_redirect);
|
||||
mg_http_send_redirect(nc_http, 301, mg_mk_str(s_redirect), mg_mk_str(NULL));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
52
src/web_server.h
Normal file
52
src/web_server.h
Normal file
@ -0,0 +1,52 @@
|
||||
/* myMPD
|
||||
(c) 2018-2019 Juergen Mang <mail@jcgames.de>
|
||||
This project's homepage is: https://github.com/jcorporation/mympd
|
||||
|
||||
myMPD ist fork of:
|
||||
|
||||
ympd
|
||||
(c) 2013-2014 Andrew Karpow <andy@ndyk.de>
|
||||
This project's homepage is: http://www.ympd.org
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __WEB_SERVER_H__
|
||||
#define __WEB_SERVER_H__
|
||||
|
||||
#include "../dist/src/mongoose/mongoose.h"
|
||||
#include "tiny_queue.h"
|
||||
|
||||
tiny_queue_t *web_server_queue;
|
||||
|
||||
struct work_request_t {
|
||||
unsigned long conn_id; // needed to identify the connection where to send the reply
|
||||
char data[1000];
|
||||
int length;
|
||||
} work_request_t;
|
||||
|
||||
struct work_result_t {
|
||||
unsigned long conn_id; // needed to identify the connection where to send the reply
|
||||
char data[MAX_SIZE];
|
||||
int length;
|
||||
} work_result_t;
|
||||
|
||||
struct mg_serve_http_opts s_http_server_opts;
|
||||
|
||||
int is_websocket(const struct mg_connection *nc);
|
||||
void ev_handler(struct mg_connection *nc, int ev, void *ev_data);
|
||||
void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_data);
|
||||
void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response);
|
||||
void send_api_response(struct mg_mgr *mgr, struct work_result_t *response);
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user