mirror of
https://github.com/owntone/owntone-server.git
synced 2025-03-31 09:43:45 -04:00
[jsonapi] Extend search endpoint to support queries in smartpl language
This commit is contained in:
parent
f1c7672426
commit
33459bd4ef
@ -47,12 +47,12 @@
|
|||||||
#include "misc_json.h"
|
#include "misc_json.h"
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "remote_pairing.h"
|
#include "remote_pairing.h"
|
||||||
|
#include "smartpl_query.h"
|
||||||
#ifdef HAVE_SPOTIFY_H
|
#ifdef HAVE_SPOTIFY_H
|
||||||
# include "spotify_webapi.h"
|
# include "spotify_webapi.h"
|
||||||
# include "spotify.h"
|
# include "spotify.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* -------------------------------- HELPERS --------------------------------- */
|
/* -------------------------------- HELPERS --------------------------------- */
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
@ -2069,125 +2069,283 @@ jsonapi_reply_library_playlist_tracks(struct httpd_request *hreq)
|
|||||||
return HTTP_OK;
|
return HTTP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
search_tracks(json_object *reply, struct httpd_request *hreq, const char *param_query, struct smartpl *smartpl_expression)
|
||||||
|
{
|
||||||
|
json_object *type;
|
||||||
|
json_object *items;
|
||||||
|
struct query_params query_params;
|
||||||
|
int total;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(&query_params, 0, sizeof(struct query_params));
|
||||||
|
|
||||||
|
type = json_object_new_object();
|
||||||
|
json_object_object_add(reply, "tracks", type);
|
||||||
|
items = json_object_new_array();
|
||||||
|
json_object_object_add(type, "items", items);
|
||||||
|
|
||||||
|
query_params.type = Q_ITEMS;
|
||||||
|
query_params.sort = S_NAME;
|
||||||
|
|
||||||
|
ret = query_params_limit_set(&query_params, hreq);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (param_query)
|
||||||
|
{
|
||||||
|
query_params.filter = db_mprintf("(f.title LIKE '%%%q%%')", param_query);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query_params.filter = strdup(smartpl_expression->query_where);
|
||||||
|
query_params.orderby = safe_strdup(smartpl_expression->order_by);
|
||||||
|
|
||||||
|
if (smartpl_expression->limit > 0)
|
||||||
|
{
|
||||||
|
query_params.idx_type = I_SUB;
|
||||||
|
query_params.limit = smartpl_expression->limit;
|
||||||
|
query_params.offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fetch_tracks(&query_params, items, &total);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
json_object_object_add(type, "total", json_object_new_int(total));
|
||||||
|
json_object_object_add(type, "offset", json_object_new_int(query_params.offset));
|
||||||
|
json_object_object_add(type, "limit", json_object_new_int(query_params.limit));
|
||||||
|
|
||||||
|
out:
|
||||||
|
free_query_params(&query_params, 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
search_artists(json_object *reply, struct httpd_request *hreq, const char *param_query, struct smartpl *smartpl_expression)
|
||||||
|
{
|
||||||
|
json_object *type;
|
||||||
|
json_object *items;
|
||||||
|
struct query_params query_params;
|
||||||
|
int total;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(&query_params, 0, sizeof(struct query_params));
|
||||||
|
|
||||||
|
ret = query_params_limit_set(&query_params, hreq);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
type = json_object_new_object();
|
||||||
|
json_object_object_add(reply, "artists", type);
|
||||||
|
items = json_object_new_array();
|
||||||
|
json_object_object_add(type, "items", items);
|
||||||
|
|
||||||
|
query_params.type = Q_GROUP_ARTISTS;
|
||||||
|
query_params.sort = S_ARTIST;
|
||||||
|
|
||||||
|
ret = query_params_limit_set(&query_params, hreq);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (param_query)
|
||||||
|
{
|
||||||
|
query_params.filter = db_mprintf("(f.album_artist LIKE '%%%q%%')", param_query);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query_params.filter = strdup(smartpl_expression->query_where);
|
||||||
|
query_params.having = safe_strdup(smartpl_expression->having);
|
||||||
|
query_params.orderby = safe_strdup(smartpl_expression->order_by);
|
||||||
|
|
||||||
|
if (smartpl_expression->limit > 0)
|
||||||
|
{
|
||||||
|
query_params.idx_type = I_SUB;
|
||||||
|
query_params.limit = smartpl_expression->limit;
|
||||||
|
query_params.offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fetch_artists(&query_params, items, &total);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
json_object_object_add(type, "total", json_object_new_int(total));
|
||||||
|
json_object_object_add(type, "offset", json_object_new_int(query_params.offset));
|
||||||
|
json_object_object_add(type, "limit", json_object_new_int(query_params.limit));
|
||||||
|
|
||||||
|
out:
|
||||||
|
free_query_params(&query_params, 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
search_albums(json_object *reply, struct httpd_request *hreq, const char *param_query, struct smartpl *smartpl_expression)
|
||||||
|
{
|
||||||
|
json_object *type;
|
||||||
|
json_object *items;
|
||||||
|
struct query_params query_params;
|
||||||
|
int total;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memset(&query_params, 0, sizeof(struct query_params));
|
||||||
|
|
||||||
|
ret = query_params_limit_set(&query_params, hreq);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
type = json_object_new_object();
|
||||||
|
json_object_object_add(reply, "albums", type);
|
||||||
|
items = json_object_new_array();
|
||||||
|
json_object_object_add(type, "items", items);
|
||||||
|
|
||||||
|
query_params.type = Q_GROUP_ALBUMS;
|
||||||
|
query_params.sort = S_ALBUM;
|
||||||
|
|
||||||
|
ret = query_params_limit_set(&query_params, hreq);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (param_query)
|
||||||
|
{
|
||||||
|
query_params.filter = db_mprintf("(f.album LIKE '%%%q%%')", param_query);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query_params.filter = strdup(smartpl_expression->query_where);
|
||||||
|
query_params.having = safe_strdup(smartpl_expression->having);
|
||||||
|
query_params.orderby = safe_strdup(smartpl_expression->order_by);
|
||||||
|
|
||||||
|
if (smartpl_expression->limit > 0)
|
||||||
|
{
|
||||||
|
query_params.idx_type = I_SUB;
|
||||||
|
query_params.limit = smartpl_expression->limit;
|
||||||
|
query_params.offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = fetch_albums(&query_params, items, &total);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
json_object_object_add(type, "total", json_object_new_int(total));
|
||||||
|
json_object_object_add(type, "offset", json_object_new_int(query_params.offset));
|
||||||
|
json_object_object_add(type, "limit", json_object_new_int(query_params.limit));
|
||||||
|
|
||||||
|
out:
|
||||||
|
free_query_params(&query_params, 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
search_playlists(json_object *reply, struct httpd_request *hreq, const char *param_query)
|
||||||
|
{
|
||||||
|
json_object *type;
|
||||||
|
json_object *items;
|
||||||
|
struct query_params query_params;
|
||||||
|
int total;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!param_query)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(&query_params, 0, sizeof(struct query_params));
|
||||||
|
|
||||||
|
ret = query_params_limit_set(&query_params, hreq);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
type = json_object_new_object();
|
||||||
|
json_object_object_add(reply, "playlists", type);
|
||||||
|
items = json_object_new_array();
|
||||||
|
json_object_object_add(type, "items", items);
|
||||||
|
|
||||||
|
query_params.type = Q_PL;
|
||||||
|
query_params.sort = S_PLAYLIST;
|
||||||
|
query_params.filter = db_mprintf("((f.type = %d OR f.type = %d) AND f.title LIKE '%%%q%%')", PL_PLAIN, PL_SMART, param_query);
|
||||||
|
|
||||||
|
ret = fetch_playlists(&query_params, items, &total);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
json_object_object_add(type, "total", json_object_new_int(total));
|
||||||
|
json_object_object_add(type, "offset", json_object_new_int(query_params.offset));
|
||||||
|
json_object_object_add(type, "limit", json_object_new_int(query_params.limit));
|
||||||
|
|
||||||
|
out:
|
||||||
|
free_query_params(&query_params, 1);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
jsonapi_reply_search(struct httpd_request *hreq)
|
jsonapi_reply_search(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
const char *param_type;
|
const char *param_type;
|
||||||
const char *param_query;
|
const char *param_query;
|
||||||
struct query_params query_params;
|
const char *param_expression;
|
||||||
|
char *expression;
|
||||||
|
struct smartpl smartpl_expression;
|
||||||
json_object *reply;
|
json_object *reply;
|
||||||
json_object *type;
|
|
||||||
json_object *items;
|
|
||||||
int total;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
reply = NULL;
|
reply = NULL;
|
||||||
|
|
||||||
param_type = evhttp_find_header(hreq->query, "type");
|
param_type = evhttp_find_header(hreq->query, "type");
|
||||||
param_query = evhttp_find_header(hreq->query, "query");
|
param_query = evhttp_find_header(hreq->query, "query");
|
||||||
|
param_expression = evhttp_find_header(hreq->query, "expression");
|
||||||
|
|
||||||
if (!param_type || !param_query)
|
if (!param_type || (!param_query && !param_expression))
|
||||||
{
|
{
|
||||||
DPRINTF(E_LOG, L_WEB, "Missing request parameter\n");
|
DPRINTF(E_LOG, L_WEB, "Missing request parameter\n");
|
||||||
|
|
||||||
return HTTP_BADREQUEST;
|
return HTTP_BADREQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&query_params, 0, sizeof(struct query_params));
|
memset(&smartpl_expression, 0, sizeof(struct smartpl));
|
||||||
|
|
||||||
ret = query_params_limit_set(&query_params, hreq);
|
if (param_expression)
|
||||||
if (ret < 0)
|
{
|
||||||
goto error;
|
expression = safe_asprintf("\"query\" { %s }", param_expression);
|
||||||
|
ret = smartpl_query_parse_string(&smartpl_expression, expression);
|
||||||
|
free(expression);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return HTTP_BADREQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
reply = json_object_new_object();
|
reply = json_object_new_object();
|
||||||
|
|
||||||
if (strstr(param_type, "track"))
|
if (strstr(param_type, "track"))
|
||||||
{
|
{
|
||||||
type = json_object_new_object();
|
ret = search_tracks(reply, hreq, param_query, &smartpl_expression);
|
||||||
json_object_object_add(reply, "tracks", type);
|
|
||||||
items = json_object_new_array();
|
|
||||||
json_object_object_add(type, "items", items);
|
|
||||||
|
|
||||||
|
|
||||||
query_params.type = Q_ITEMS;
|
|
||||||
query_params.sort = S_NAME;
|
|
||||||
query_params.filter = db_mprintf("(f.title LIKE '%%%q%%')", param_query);
|
|
||||||
|
|
||||||
ret = fetch_tracks(&query_params, items, &total);
|
|
||||||
free(query_params.filter);
|
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
json_object_object_add(type, "total", json_object_new_int(total));
|
|
||||||
json_object_object_add(type, "offset", json_object_new_int(query_params.offset));
|
|
||||||
json_object_object_add(type, "limit", json_object_new_int(query_params.limit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strstr(param_type, "artist"))
|
if (strstr(param_type, "artist"))
|
||||||
{
|
{
|
||||||
type = json_object_new_object();
|
ret = search_artists(reply, hreq, param_query, &smartpl_expression);
|
||||||
json_object_object_add(reply, "artists", type);
|
|
||||||
items = json_object_new_array();
|
|
||||||
json_object_object_add(type, "items", items);
|
|
||||||
|
|
||||||
query_params.type = Q_GROUP_ARTISTS;
|
|
||||||
query_params.sort = S_ARTIST;
|
|
||||||
query_params.filter = db_mprintf("(f.album_artist LIKE '%%%q%%')", param_query);
|
|
||||||
|
|
||||||
ret = fetch_artists(&query_params, items, &total);
|
|
||||||
free(query_params.filter);
|
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
json_object_object_add(type, "total", json_object_new_int(total));
|
|
||||||
json_object_object_add(type, "offset", json_object_new_int(query_params.offset));
|
|
||||||
json_object_object_add(type, "limit", json_object_new_int(query_params.limit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strstr(param_type, "album"))
|
if (strstr(param_type, "album"))
|
||||||
{
|
{
|
||||||
type = json_object_new_object();
|
ret = search_albums(reply, hreq, param_query, &smartpl_expression);
|
||||||
json_object_object_add(reply, "albums", type);
|
|
||||||
items = json_object_new_array();
|
|
||||||
json_object_object_add(type, "items", items);
|
|
||||||
|
|
||||||
query_params.type = Q_GROUP_ALBUMS;
|
|
||||||
query_params.sort = S_ALBUM;
|
|
||||||
query_params.filter = db_mprintf("(f.album LIKE '%%%q%%')", param_query);
|
|
||||||
|
|
||||||
ret = fetch_albums(&query_params, items, &total);
|
|
||||||
free(query_params.filter);
|
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
json_object_object_add(type, "total", json_object_new_int(total));
|
|
||||||
json_object_object_add(type, "offset", json_object_new_int(query_params.offset));
|
|
||||||
json_object_object_add(type, "limit", json_object_new_int(query_params.limit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strstr(param_type, "playlist"))
|
if (strstr(param_type, "playlist") && param_query)
|
||||||
{
|
{
|
||||||
type = json_object_new_object();
|
ret = search_playlists(reply, hreq, param_query);
|
||||||
json_object_object_add(reply, "playlists", type);
|
|
||||||
items = json_object_new_array();
|
|
||||||
json_object_object_add(type, "items", items);
|
|
||||||
|
|
||||||
query_params.type = Q_PL;
|
|
||||||
query_params.sort = S_PLAYLIST;
|
|
||||||
query_params.filter = db_mprintf("((f.type = %d OR f.type = %d) AND f.title LIKE '%%%q%%')", PL_PLAIN, PL_SMART, param_query);
|
|
||||||
|
|
||||||
ret = fetch_playlists(&query_params, items, &total);
|
|
||||||
free(query_params.filter);
|
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
json_object_object_add(type, "total", json_object_new_int(total));
|
|
||||||
json_object_object_add(type, "offset", json_object_new_int(query_params.offset));
|
|
||||||
json_object_object_add(type, "limit", json_object_new_int(query_params.limit));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply));
|
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply));
|
||||||
@ -2196,6 +2354,7 @@ jsonapi_reply_search(struct httpd_request *hreq)
|
|||||||
|
|
||||||
error:
|
error:
|
||||||
jparse_free(reply);
|
jparse_free(reply);
|
||||||
|
free_smartpl(&smartpl_expression, 1);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return HTTP_INTERNAL;
|
return HTTP_INTERNAL;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user