mirror of
https://github.com/owntone/owntone-server.git
synced 2025-11-07 21:03:00 -05:00
Merge pull request #499 from chme/jsonapi
New endpoints for the JSON api (library, queue)
This commit is contained in:
62
src/db.c
62
src/db.c
@@ -333,6 +333,68 @@ static const struct browse_clause browse_clause[] =
|
||||
{ "f.path, f.path", "f.path", "f.path" },
|
||||
};
|
||||
|
||||
|
||||
struct media_kind_label {
|
||||
enum media_kind type;
|
||||
const char *label;
|
||||
};
|
||||
|
||||
/* Keep in sync with enum media_kind */
|
||||
static const struct media_kind_label media_kind_labels[] =
|
||||
{
|
||||
{ MEDIA_KIND_MUSIC, "music" },
|
||||
{ MEDIA_KIND_MOVIE, "movie" },
|
||||
{ MEDIA_KIND_PODCAST, "podcast" },
|
||||
{ MEDIA_KIND_AUDIOBOOK, "audiobook" },
|
||||
{ MEDIA_KIND_MUSICVIDEO, "musicvideo" },
|
||||
{ MEDIA_KIND_TVSHOW, "tvshow" },
|
||||
};
|
||||
|
||||
const char *
|
||||
db_media_kind_label(enum media_kind media_kind)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(media_kind_labels); i++)
|
||||
{
|
||||
if (media_kind == media_kind_labels[i].type)
|
||||
return media_kind_labels[i].label;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum media_kind
|
||||
db_media_kind_enum(const char *label)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!label)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(media_kind_labels); i++)
|
||||
{
|
||||
if (strcmp(label, media_kind_labels[i].label) == 0)
|
||||
return media_kind_labels[i].type;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Keep in sync with enum data_kind */
|
||||
static char *data_kind_label[] = { "file", "url", "spotify", "pipe" };
|
||||
|
||||
const char *
|
||||
db_data_kind_label(enum data_kind data_kind)
|
||||
{
|
||||
if (data_kind < ARRAY_SIZE(data_kind_label))
|
||||
{
|
||||
return data_kind_label[data_kind];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Shuffle RNG state */
|
||||
struct rng_ctx shuffle_rng;
|
||||
|
||||
|
||||
11
src/db.h
11
src/db.h
@@ -105,6 +105,7 @@ struct pairing_info {
|
||||
char *guid;
|
||||
};
|
||||
|
||||
/* Keep in sync with media_kind_labels[] */
|
||||
enum media_kind {
|
||||
MEDIA_KIND_MUSIC = 1,
|
||||
MEDIA_KIND_MOVIE = 2,
|
||||
@@ -114,6 +115,13 @@ enum media_kind {
|
||||
MEDIA_KIND_TVSHOW = 64,
|
||||
};
|
||||
|
||||
const char *
|
||||
db_media_kind_label(enum media_kind media_kind);
|
||||
|
||||
enum media_kind
|
||||
db_media_kind_enum(const char *label);
|
||||
|
||||
/* Keep in sync with data_kind_label[] */
|
||||
enum data_kind {
|
||||
DATA_KIND_FILE = 0, /* normal file */
|
||||
DATA_KIND_HTTP = 1, /* network stream (radio) */
|
||||
@@ -121,6 +129,9 @@ enum data_kind {
|
||||
DATA_KIND_PIPE = 3, /* iTunes has no pipe data kind, but we use 3 */
|
||||
};
|
||||
|
||||
const char *
|
||||
db_data_kind_label(enum data_kind data_kind);
|
||||
|
||||
/* Note that fields marked as integers in the metadata map in filescanner_ffmpeg must be uint32_t here */
|
||||
struct media_file_info {
|
||||
char *path;
|
||||
|
||||
108
src/httpd.c
108
src/httpd.c
@@ -255,12 +255,25 @@ httpd_fixup_uri(struct evhttp_request *req)
|
||||
|
||||
/* --------------------------- REQUEST HELPERS ------------------------------ */
|
||||
|
||||
|
||||
|
||||
void
|
||||
httpd_redirect_to_admin(struct evhttp_request *req)
|
||||
{
|
||||
struct evkeyvalq *headers;
|
||||
|
||||
headers = evhttp_request_get_output_headers(req);
|
||||
evhttp_add_header(headers, "Location", "/admin.html");
|
||||
|
||||
httpd_send_reply(req, HTTP_MOVETEMP, "Moved", NULL, HTTPD_SEND_NO_GZIP);
|
||||
}
|
||||
|
||||
static void
|
||||
serve_file(struct evhttp_request *req, const char *uri)
|
||||
{
|
||||
char *ext;
|
||||
char path[PATH_MAX];
|
||||
char *deref;
|
||||
char deref[PATH_MAX];
|
||||
char *ctype;
|
||||
struct evbuffer *evbuf;
|
||||
struct evkeyvalq *input_headers;
|
||||
@@ -272,6 +285,7 @@ serve_file(struct evhttp_request *req, const char *uri)
|
||||
const char *modified_since;
|
||||
char last_modified[1000];
|
||||
struct tm *tm_modified;
|
||||
bool slashed;
|
||||
int ret;
|
||||
|
||||
/* Check authentication */
|
||||
@@ -298,16 +312,9 @@ serve_file(struct evhttp_request *req, const char *uri)
|
||||
return;
|
||||
}
|
||||
|
||||
if (S_ISDIR(sb.st_mode))
|
||||
if (S_ISLNK(sb.st_mode))
|
||||
{
|
||||
httpd_redirect_to_index(req, uri);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (S_ISLNK(sb.st_mode))
|
||||
{
|
||||
deref = realpath(path, NULL);
|
||||
if (!deref)
|
||||
if (!realpath(path, deref))
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTPD, "Could not dereference %s: %s\n", path, strerror(errno));
|
||||
|
||||
@@ -322,12 +329,10 @@ serve_file(struct evhttp_request *req, const char *uri)
|
||||
|
||||
httpd_send_error(req, HTTP_NOTFOUND, "Not Found");
|
||||
|
||||
free(deref);
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(path, deref);
|
||||
free(deref);
|
||||
|
||||
ret = stat(path, &sb);
|
||||
if (ret < 0)
|
||||
@@ -338,13 +343,38 @@ serve_file(struct evhttp_request *req, const char *uri)
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (S_ISDIR(sb.st_mode))
|
||||
{
|
||||
httpd_redirect_to_index(req, uri);
|
||||
if (S_ISDIR(sb.st_mode))
|
||||
{
|
||||
slashed = (path[strlen(path) - 1] == '/');
|
||||
|
||||
ret = snprintf(deref, sizeof(deref), "%s%sindex.html", path, (slashed) ? "" : "/");
|
||||
if ((ret < 0) || (ret >= sizeof(deref)))
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTPD, "Redirection URL exceeds buffer length\n");
|
||||
|
||||
httpd_send_error(req, HTTP_NOTFOUND, "Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(path, deref);
|
||||
|
||||
ret = stat(path, &sb);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (strcmp(uri, "/") == 0)
|
||||
{
|
||||
httpd_redirect_to_admin(req);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTPD, "Could not stat() %s: %s\n", path, strerror(errno));
|
||||
httpd_send_error(req, HTTP_NOTFOUND, "Not Found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (path_is_legal(path) != 0)
|
||||
@@ -713,9 +743,7 @@ httpd_gen_cb(struct evhttp_request *req, void *arg)
|
||||
output_headers = evhttp_request_get_output_headers(req);
|
||||
|
||||
evhttp_add_header(output_headers, "Access-Control-Allow-Origin", allow_origin);
|
||||
|
||||
// Allow only GET method and authorization header in cross origin requests
|
||||
evhttp_add_header(output_headers, "Access-Control-Allow-Method", "GET");
|
||||
evhttp_add_header(output_headers, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
||||
evhttp_add_header(output_headers, "Access-Control-Allow-Headers", "authorization");
|
||||
|
||||
// In this case there is no reason to go through httpd_send_reply
|
||||
@@ -732,12 +760,17 @@ httpd_gen_cb(struct evhttp_request *req, void *arg)
|
||||
}
|
||||
|
||||
parsed = httpd_uri_parse(uri);
|
||||
if (!parsed || !parsed->path || (strcmp(parsed->path, "/") == 0))
|
||||
if (!parsed || !parsed->path)
|
||||
{
|
||||
httpd_redirect_to_admin(req);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (strcmp(parsed->path, "/") == 0)
|
||||
{
|
||||
goto serve_file;
|
||||
}
|
||||
|
||||
/* Dispatch protocol-specific handlers */
|
||||
if (dacp_is_request(parsed->path))
|
||||
{
|
||||
@@ -773,6 +806,7 @@ httpd_gen_cb(struct evhttp_request *req, void *arg)
|
||||
DPRINTF(E_DBG, L_HTTPD, "HTTP request: '%s'\n", parsed->uri);
|
||||
|
||||
/* Serve web interface files */
|
||||
serve_file:
|
||||
serve_file(req, parsed->path);
|
||||
|
||||
out:
|
||||
@@ -1379,42 +1413,6 @@ httpd_send_error(struct evhttp_request* req, int error, const char* reason)
|
||||
evbuffer_free(evbuf);
|
||||
}
|
||||
|
||||
void
|
||||
httpd_redirect_to_admin(struct evhttp_request *req)
|
||||
{
|
||||
struct evkeyvalq *headers;
|
||||
|
||||
headers = evhttp_request_get_output_headers(req);
|
||||
evhttp_add_header(headers, "Location", "/admin.html");
|
||||
|
||||
httpd_send_reply(req, HTTP_MOVETEMP, "Moved", NULL, HTTPD_SEND_NO_GZIP);
|
||||
}
|
||||
|
||||
void
|
||||
httpd_redirect_to_index(struct evhttp_request *req, const char *uri)
|
||||
{
|
||||
struct evkeyvalq *headers;
|
||||
char buf[256];
|
||||
int slashed;
|
||||
int ret;
|
||||
|
||||
slashed = (uri[strlen(uri) - 1] == '/');
|
||||
|
||||
ret = snprintf(buf, sizeof(buf), "%s%sindex.html", uri, (slashed) ? "" : "/");
|
||||
if ((ret < 0) || (ret >= sizeof(buf)))
|
||||
{
|
||||
DPRINTF(E_LOG, L_HTTPD, "Redirection URL exceeds buffer length\n");
|
||||
|
||||
httpd_send_error(req, HTTP_NOTFOUND, "Not Found");
|
||||
return;
|
||||
}
|
||||
|
||||
headers = evhttp_request_get_output_headers(req);
|
||||
evhttp_add_header(headers, "Location", buf);
|
||||
|
||||
httpd_send_reply(req, HTTP_MOVETEMP, "Moved", NULL, HTTPD_SEND_NO_GZIP);
|
||||
}
|
||||
|
||||
bool
|
||||
httpd_admin_check_auth(struct evhttp_request *req)
|
||||
{
|
||||
|
||||
@@ -145,11 +145,6 @@ httpd_send_error(struct evhttp_request *req, int error, const char *reason);
|
||||
void
|
||||
httpd_redirect_to_admin(struct evhttp_request *req);
|
||||
|
||||
/*
|
||||
* Redirects to [uri]/index.html
|
||||
*/
|
||||
void
|
||||
httpd_redirect_to_index(struct evhttp_request *req, const char *uri);
|
||||
|
||||
bool
|
||||
httpd_admin_check_auth(struct evhttp_request *req);
|
||||
|
||||
1379
src/httpd_jsonapi.c
1379
src/httpd_jsonapi.c
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,7 @@
|
||||
#define STOB(s) ((s) * 4)
|
||||
#define BTOS(b) ((b) / 4)
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
|
||||
struct onekeyval {
|
||||
char *name;
|
||||
@@ -57,7 +58,6 @@ safe_strdup(const char *str);
|
||||
char *
|
||||
safe_asprintf(const char *fmt, ...);
|
||||
|
||||
|
||||
/* Key/value functions */
|
||||
struct keyval *
|
||||
keyval_alloc(void);
|
||||
|
||||
Reference in New Issue
Block a user