genre search functionality (#559)
* [jsonapi] Add support for listing albums for genres
This commit is contained in:
parent
48f11a8250
commit
e3ce003190
|
@ -669,6 +669,7 @@ curl -X PUT "http://localhost:3689/api/queue/items/2"
|
|||
| GET | [/api/library/albums](#list-albums) | Get a list of albums |
|
||||
| GET | [/api/library/albums/{id}](#get-an-album) | Get an album |
|
||||
| GET | [/api/library/albums/{id}/tracks](#list-album-tracks) | Get list of tracks for an album |
|
||||
| GET | [/api/library/genres](#list-genres) | Get list of genres |
|
||||
| GET | [/api/library/count](#get-count-of-tracks-artists-and-albums) | Get count of tracks, artists and albums |
|
||||
|
||||
|
||||
|
@ -1159,6 +1160,136 @@ curl -X GET "http://localhost:3689/api/library/albums/1/tracks"
|
|||
```
|
||||
|
||||
|
||||
### list genres
|
||||
|
||||
Get list of genres
|
||||
|
||||
**Endpoint**
|
||||
|
||||
```
|
||||
GET /api/library/genres
|
||||
```
|
||||
**Response**
|
||||
|
||||
| Key | Type | Value |
|
||||
| --------------- | -------- | ----------------------------------------- |
|
||||
| items | array | Array of [`genre`](#genre-object) objects |
|
||||
| total | integer | Total number of genres in the library |
|
||||
| offset | integer | Requested offset of the first genre |
|
||||
| limit | integer | Requested maximum number of genres |
|
||||
|
||||
|
||||
**Example**
|
||||
|
||||
```
|
||||
curl -X GET "http://localhost:3689/api/library/genres"
|
||||
```
|
||||
|
||||
```
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"name": "Classical"
|
||||
},
|
||||
{
|
||||
"name": "Drum & Bass"
|
||||
},
|
||||
{
|
||||
"name": "Pop"
|
||||
},
|
||||
{
|
||||
"name": "Rock/Pop"
|
||||
},
|
||||
{
|
||||
"name": "'90s Alternative"
|
||||
}
|
||||
],
|
||||
"total": 5,
|
||||
"offset": 0,
|
||||
"limit": -1
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### List albums for genre
|
||||
|
||||
Lists the albums in a genre
|
||||
|
||||
**Endpoint**
|
||||
|
||||
```
|
||||
GET api/search?type=albums&expression=genre+is+\"{genre name}\""
|
||||
```
|
||||
|
||||
**Query parameters**
|
||||
|
||||
| Parameter | Value |
|
||||
| --------------- | ----------------------------------------------------------- |
|
||||
| genre | genre name (uri encoded and html esc seq for chars: '/&) |
|
||||
| offset | *(Optional)* Offset of the first album to return |
|
||||
| limit | *(Optional)* Maximum number of albums to return |
|
||||
|
||||
**Response**
|
||||
|
||||
| Key | Type | Value |
|
||||
| --------------- | -------- | ----------------------------------------- |
|
||||
| items | array | Array of [`album`](#album-object) objects |
|
||||
| total | integer | Total number of albums in the library |
|
||||
| offset | integer | Requested offset of the first albums |
|
||||
| limit | integer | Requested maximum number of albums |
|
||||
|
||||
|
||||
**Example**
|
||||
|
||||
```
|
||||
curl -X GET "http://localhost:3689/api/search?type=albums&expression=genre+is+\"Pop\""
|
||||
curl -X GET "http://localhost:3689/api/search?type=albums&expression=genre+is+\"Rock%2FPop\"" # Rock/Pop
|
||||
curl -X GET "http://localhost:3689/api/search?type=albums&expression=genre+is+\"Drum%20%26%20Bass\"" # Drum & Bass
|
||||
curl -X GET "http://localhost:3689/api/search?type=albums&expression=genre+is+\"%2790s%20Alternative\"" # '90 Alternative
|
||||
```
|
||||
|
||||
```
|
||||
{
|
||||
"albums": {
|
||||
"items": [
|
||||
{
|
||||
"id": "320189328729146437",
|
||||
"name": "Best Ever",
|
||||
"name_sort": "Best Ever",
|
||||
"artist": "ABC",
|
||||
"artist_id": "8760559201889050080",
|
||||
"track_count": 1,
|
||||
"length_ms": 3631,
|
||||
"uri": "library:album:320189328729146437"
|
||||
},
|
||||
{
|
||||
"id": "7964595866631625723",
|
||||
"name": "Greatest Hits",
|
||||
"name_sort": "Greatest Hits",
|
||||
"artist": "Marvin Gaye",
|
||||
"artist_id": "5261930703203735930",
|
||||
"track_count": 2,
|
||||
"length_ms": 7262,
|
||||
"uri": "library:album:7964595866631625723"
|
||||
},
|
||||
{
|
||||
"id": "3844610748145176456",
|
||||
"name": "The Very Best of Etta",
|
||||
"name_sort": "Very Best of Etta",
|
||||
"artist": "Etta James",
|
||||
"artist_id": "2627182178555864595",
|
||||
"track_count": 1,
|
||||
"length_ms": 177926,
|
||||
"uri": "library:album:3844610748145176456"
|
||||
}
|
||||
],
|
||||
"total": 3,
|
||||
"offset": 0,
|
||||
"limit": -1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Get count of tracks, artists and albums
|
||||
|
||||
Get information about the number of tracks, artists and albums and the total playtime
|
||||
|
@ -1206,14 +1337,14 @@ curl -X GET "http://localhost:3689/api/library/count?expression=data_kind+is+fil
|
|||
|
||||
| Method | Endpoint | Description |
|
||||
| --------- | ----------------------------------------------------------- | ------------------------------------ |
|
||||
| GET | [/api/search](#search-by-search-term) | Search for playlists, artists, albums, tracks by a simple search term |
|
||||
| GET | [/api/search](#search-by-search-term) | Search for playlists, artists, albums, tracks,genres by a simple search term |
|
||||
| GET | [/api/search](#search-by-query-language) | Search by complex query expression |
|
||||
|
||||
|
||||
|
||||
### Search by search term
|
||||
|
||||
Search for playlists, artists, albums, tracks that include the given query in their title (case insensitive matching).
|
||||
Search for playlists, artists, albums, tracks, genres that include the given query in their title (case insensitive matching).
|
||||
|
||||
**Endpoint**
|
||||
|
||||
|
@ -1226,7 +1357,7 @@ GET /api/search
|
|||
| Parameter | Value |
|
||||
| --------------- | ----------------------------------------------------------- |
|
||||
| query | The search keyword |
|
||||
| type | Comma separated list of the result types (`playlist`, `artist`, `album`, `track`) |
|
||||
| type | Comma separated list of the result types (`playlist`, `artist`, `album`, `track`, `genre`) |
|
||||
| media_kind | *(Optional)* Filter results by media kind (`music`, `movie`, `podcast`, `audiobook`, `musicvideo`, `tvshow`). Filter only applies to artist, album and track result types. |
|
||||
| offset | *(Optional)* Offset of the first item to return for each type |
|
||||
| limit | *(Optional)* Maximum number of items to return for each type |
|
||||
|
@ -1594,3 +1725,10 @@ curl --include \
|
|||
| offset | integer | Requested offset of the first item |
|
||||
| limit | integer | Requested maximum number of items |
|
||||
|
||||
|
||||
### `genre` object
|
||||
|
||||
| Key | Type | Value |
|
||||
| --------------- | -------- | ----------------------------------------- |
|
||||
| name | string | Name of genre |
|
||||
|
||||
|
|
|
@ -234,12 +234,29 @@ playlist_to_json(struct db_playlist_info *dbpli)
|
|||
return item;
|
||||
}
|
||||
|
||||
static json_object *
|
||||
genre_to_json(const char *genre)
|
||||
{
|
||||
json_object *item;
|
||||
|
||||
if (genre == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
item = json_object_new_object();
|
||||
safe_json_add_string(item, "name", genre);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
fetch_tracks(struct query_params *query_params, json_object *items, int *total)
|
||||
{
|
||||
struct db_media_file_info dbmfi;
|
||||
json_object *item;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
ret = db_query_start(query_params);
|
||||
if (ret < 0)
|
||||
|
@ -328,6 +345,7 @@ fetch_artist(const char *artist_id)
|
|||
|
||||
error:
|
||||
db_query_end(&query_params);
|
||||
free(query_params.filter);
|
||||
|
||||
return artist;
|
||||
}
|
||||
|
@ -394,6 +412,7 @@ fetch_album(const char *album_id)
|
|||
|
||||
error:
|
||||
db_query_end(&query_params);
|
||||
free(query_params.filter);
|
||||
|
||||
return album;
|
||||
}
|
||||
|
@ -456,10 +475,45 @@ fetch_playlist(const char *playlist_id)
|
|||
|
||||
error:
|
||||
db_query_end(&query_params);
|
||||
free(query_params.filter);
|
||||
|
||||
return playlist;
|
||||
}
|
||||
|
||||
static int
|
||||
fetch_genres(struct query_params *query_params, json_object *items, int *total)
|
||||
{
|
||||
json_object *item;
|
||||
int ret;
|
||||
char *genre;
|
||||
char *sort_item;
|
||||
|
||||
ret = db_query_start(query_params);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
while (((ret = db_query_fetch_string_sort(query_params, &genre, &sort_item)) == 0) && (genre))
|
||||
{
|
||||
item = genre_to_json(genre);
|
||||
if (!item)
|
||||
{
|
||||
ret = -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
json_object_array_add(items, item);
|
||||
}
|
||||
|
||||
if (total)
|
||||
*total = query_params->results;
|
||||
|
||||
error:
|
||||
db_query_end(query_params);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
query_params_limit_set(struct query_params *query_params, struct httpd_request *hreq)
|
||||
{
|
||||
|
@ -567,6 +621,7 @@ jsonapi_reply_library(struct httpd_request *hreq)
|
|||
struct filecount_info fci;
|
||||
json_object *jreply;
|
||||
int ret;
|
||||
char *s;
|
||||
|
||||
|
||||
CHECK_NULL(L_WEB, jreply = json_object_new_object());
|
||||
|
@ -586,8 +641,10 @@ jsonapi_reply_library(struct httpd_request *hreq)
|
|||
DPRINTF(E_LOG, L_WEB, "library: failed to get file count info\n");
|
||||
}
|
||||
|
||||
safe_json_add_time_from_string(jreply, "started_at", db_admin_get(DB_ADMIN_START_TIME), true);
|
||||
safe_json_add_time_from_string(jreply, "updated_at", db_admin_get(DB_ADMIN_DB_UPDATE), true);
|
||||
safe_json_add_time_from_string(jreply, "started_at", (s = db_admin_get(DB_ADMIN_START_TIME)), true);
|
||||
free(s);
|
||||
safe_json_add_time_from_string(jreply, "updated_at", (s = db_admin_get(DB_ADMIN_DB_UPDATE)), true);
|
||||
free(s);
|
||||
|
||||
json_object_object_add(jreply, "updating", json_object_new_boolean(library_is_scanning()));
|
||||
|
||||
|
@ -2356,6 +2413,75 @@ jsonapi_reply_library_playlist_tracks(struct httpd_request *hreq)
|
|||
return HTTP_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
jsonapi_reply_library_genres(struct httpd_request *hreq)
|
||||
{
|
||||
time_t db_update;
|
||||
struct query_params query_params;
|
||||
const char *param;
|
||||
enum media_kind media_kind;
|
||||
json_object *reply;
|
||||
json_object *items;
|
||||
int total;
|
||||
int ret;
|
||||
|
||||
|
||||
db_update = (time_t) db_admin_getint64(DB_ADMIN_DB_UPDATE);
|
||||
if (db_update && httpd_request_not_modified_since(hreq->req, &db_update))
|
||||
return HTTP_NOTMODIFIED;
|
||||
|
||||
media_kind = 0;
|
||||
param = evhttp_find_header(hreq->query, "media_kind");
|
||||
if (param)
|
||||
{
|
||||
media_kind = db_media_kind_enum(param);
|
||||
if (!media_kind)
|
||||
{
|
||||
DPRINTF(E_LOG, L_WEB, "Invalid media kind '%s'\n", param);
|
||||
return HTTP_BADREQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
reply = json_object_new_object();
|
||||
items = json_object_new_array();
|
||||
json_object_object_add(reply, "items", items);
|
||||
|
||||
memset(&query_params, 0, sizeof(struct query_params));
|
||||
|
||||
ret = query_params_limit_set(&query_params, hreq);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
query_params.type = Q_BROWSE_GENRES;
|
||||
query_params.idx_type = I_NONE;
|
||||
|
||||
if (media_kind)
|
||||
query_params.filter = db_mprintf("(f.media_kind = %d)", media_kind);
|
||||
|
||||
ret = fetch_genres(&query_params, items, NULL);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
else
|
||||
total = json_object_array_length(items);
|
||||
|
||||
json_object_object_add(reply, "total", json_object_new_int(total));
|
||||
json_object_object_add(reply, "offset", json_object_new_int(query_params.offset));
|
||||
json_object_object_add(reply, "limit", json_object_new_int(query_params.limit));
|
||||
|
||||
ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply));
|
||||
if (ret < 0)
|
||||
DPRINTF(E_LOG, L_WEB, "browse: Couldn't add genres to response buffer.\n");
|
||||
|
||||
error:
|
||||
jparse_free(reply);
|
||||
free_query_params(&query_params, 1);
|
||||
|
||||
if (ret < 0)
|
||||
return HTTP_INTERNAL;
|
||||
|
||||
return HTTP_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
jsonapi_reply_library_count(struct httpd_request *hreq)
|
||||
{
|
||||
|
@ -2679,6 +2805,7 @@ jsonapi_reply_search(struct httpd_request *hreq)
|
|||
if (param_expression)
|
||||
{
|
||||
expression = safe_asprintf("\"query\" { %s }", param_expression);
|
||||
|
||||
ret = smartpl_query_parse_string(&smartpl_expression, expression);
|
||||
free(expression);
|
||||
|
||||
|
@ -2779,6 +2906,7 @@ static struct httpd_uri_map adm_handlers[] =
|
|||
{ EVHTTP_REQ_GET, "^/api/library/albums$", jsonapi_reply_library_albums },
|
||||
{ EVHTTP_REQ_GET, "^/api/library/albums/[[:digit:]]+$", jsonapi_reply_library_album },
|
||||
{ EVHTTP_REQ_GET, "^/api/library/albums/[[:digit:]]+/tracks$", jsonapi_reply_library_album_tracks },
|
||||
{ EVHTTP_REQ_GET, "^/api/library/genres$", jsonapi_reply_library_genres},
|
||||
{ EVHTTP_REQ_GET, "^/api/library/count$", jsonapi_reply_library_count },
|
||||
|
||||
{ EVHTTP_REQ_GET, "^/api/search$", jsonapi_reply_search },
|
||||
|
|
Loading…
Reference in New Issue