mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-14 08:15:02 -05:00
Merge pull request #906 from chme/playlist_folder2
JSON API - Support for Playlist Folders
This commit is contained in:
commit
1bf094ed81
@ -747,6 +747,7 @@ curl -X PUT "http://localhost:3689/api/queue/items/2"
|
|||||||
| GET | [/api/library/playlists](#list-playlists) | Get a list of playlists |
|
| GET | [/api/library/playlists](#list-playlists) | Get a list of playlists |
|
||||||
| GET | [/api/library/playlists/{id}](#get-a-playlist) | Get a playlist |
|
| GET | [/api/library/playlists/{id}](#get-a-playlist) | Get a playlist |
|
||||||
| GET | [/api/library/playlists/{id}/tracks](#list-playlist-tracks) | Get list of tracks for a playlist |
|
| GET | [/api/library/playlists/{id}/tracks](#list-playlist-tracks) | Get list of tracks for a playlist |
|
||||||
|
| GET | [/api/library/playlists/{id}/playlists](#list-playlists-in-a-playlist-folder) | Get list of playlists for a playlist folder |
|
||||||
| GET | [/api/library/artists](#list-artists) | Get a list of artists |
|
| GET | [/api/library/artists](#list-artists) | Get a list of artists |
|
||||||
| GET | [/api/library/artists/{id}](#get-an-artist) | Get an artist |
|
| GET | [/api/library/artists/{id}](#get-an-artist) | Get an artist |
|
||||||
| GET | [/api/library/artists/{id}/albums](#list-artist-albums) | Get list of albums for an artist |
|
| GET | [/api/library/artists/{id}/albums](#list-artist-albums) | Get list of albums for an artist |
|
||||||
@ -807,7 +808,7 @@ curl -X GET "http://localhost:3689/api/library"
|
|||||||
|
|
||||||
### List playlists
|
### List playlists
|
||||||
|
|
||||||
Lists the playlists in your library
|
Lists all playlists in your library (does not return playlist folders)
|
||||||
|
|
||||||
**Endpoint**
|
**Endpoint**
|
||||||
|
|
||||||
@ -968,6 +969,76 @@ curl -X GET "http://localhost:3689/api/library/playlists/1/tracks"
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### List playlists in a playlist folder
|
||||||
|
|
||||||
|
Lists the playlists in a playlist folder
|
||||||
|
|
||||||
|
**Note**: The root playlist folder has `id` 0.
|
||||||
|
|
||||||
|
**Endpoint**
|
||||||
|
|
||||||
|
```http
|
||||||
|
GET /api/library/playlists/{id}/playlists
|
||||||
|
```
|
||||||
|
|
||||||
|
**Path parameters**
|
||||||
|
|
||||||
|
| Parameter | Value |
|
||||||
|
| --------------- | -------------------- |
|
||||||
|
| id | Playlist id |
|
||||||
|
|
||||||
|
**Query parameters**
|
||||||
|
|
||||||
|
| Parameter | Value |
|
||||||
|
| --------------- | ----------------------------------------------------------- |
|
||||||
|
| offset | *(Optional)* Offset of the first playlist to return |
|
||||||
|
| limit | *(Optional)* Maximum number of playlist to return |
|
||||||
|
|
||||||
|
**Response**
|
||||||
|
|
||||||
|
| Key | Type | Value |
|
||||||
|
| --------------- | -------- | ------------------------------------------------ |
|
||||||
|
| items | array | Array of [`playlist`](#playlist-object) objects |
|
||||||
|
| total | integer | Total number of playlists in the playlist folder |
|
||||||
|
| offset | integer | Requested offset of the first playlist |
|
||||||
|
| limit | integer | Requested maximum number of playlist |
|
||||||
|
|
||||||
|
|
||||||
|
**Example**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -X GET "http://localhost:3689/api/library/playlists/0/tracks"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "Spotify",
|
||||||
|
"path": "spotify:playlistfolder",
|
||||||
|
"parent_id": "0",
|
||||||
|
"smart_playlist": false,
|
||||||
|
"folder": true,
|
||||||
|
"uri": "library:playlist:11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "bytefm",
|
||||||
|
"path": "/srv/music/Playlists/bytefm.m3u",
|
||||||
|
"parent_id": "0",
|
||||||
|
"smart_playlist": false,
|
||||||
|
"folder": false,
|
||||||
|
"uri": "library:playlist:8"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 2,
|
||||||
|
"offset": 0,
|
||||||
|
"limit": -1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### List artists
|
### List artists
|
||||||
|
|
||||||
Lists the artists in your library
|
Lists the artists in your library
|
||||||
@ -2203,7 +2274,10 @@ curl --include \
|
|||||||
| id | string | Playlist id |
|
| id | string | Playlist id |
|
||||||
| name | string | Playlist name |
|
| name | string | Playlist name |
|
||||||
| path | string | Path |
|
| path | string | Path |
|
||||||
|
| parent_id | integer | Playlist id of the parent (folder) playlist |
|
||||||
|
| type | string | Type of this playlist: `special`, `folder`, `smart`, `plain` |
|
||||||
| smart_playlist | boolean | `true` if playlist is a smart playlist |
|
| smart_playlist | boolean | `true` if playlist is a smart playlist |
|
||||||
|
| folder | boolean | `true` if it is a playlist folder |
|
||||||
| uri | string | Resource identifier |
|
| uri | string | Resource identifier |
|
||||||
|
|
||||||
|
|
||||||
|
14
src/db.c
14
src/db.c
@ -510,6 +510,20 @@ db_data_kind_label(enum data_kind data_kind)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Keep in sync with enum pl_type */
|
||||||
|
static char *pl_type_label[] = { "special", "folder", "smart", "plain" };
|
||||||
|
|
||||||
|
const char *
|
||||||
|
db_pl_type_label(enum pl_type pl_type)
|
||||||
|
{
|
||||||
|
if (pl_type < ARRAY_SIZE(pl_type_label))
|
||||||
|
{
|
||||||
|
return pl_type_label[pl_type];
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Shuffle RNG state */
|
/* Shuffle RNG state */
|
||||||
struct rng_ctx shuffle_rng;
|
struct rng_ctx shuffle_rng;
|
||||||
|
|
||||||
|
4
src/db.h
4
src/db.h
@ -227,6 +227,7 @@ struct media_file_info {
|
|||||||
|
|
||||||
#define mfi_offsetof(field) offsetof(struct media_file_info, field)
|
#define mfi_offsetof(field) offsetof(struct media_file_info, field)
|
||||||
|
|
||||||
|
/* Keep in sync with pl_type_label[] */
|
||||||
/* PL_SPECIAL value must be in sync with type value in Q_PL* in db_init.c */
|
/* PL_SPECIAL value must be in sync with type value in Q_PL* in db_init.c */
|
||||||
enum pl_type {
|
enum pl_type {
|
||||||
PL_SPECIAL = 0,
|
PL_SPECIAL = 0,
|
||||||
@ -236,6 +237,9 @@ enum pl_type {
|
|||||||
PL_MAX,
|
PL_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char *
|
||||||
|
db_pl_type_label(enum pl_type pl_type);
|
||||||
|
|
||||||
struct playlist_info {
|
struct playlist_info {
|
||||||
uint32_t id; /* integer id (miid) */
|
uint32_t id; /* integer id (miid) */
|
||||||
char *title; /* playlist name as displayed in iTunes (minm) */
|
char *title; /* playlist name as displayed in iTunes (minm) */
|
||||||
|
@ -281,9 +281,14 @@ playlist_to_json(struct db_playlist_info *dbpli)
|
|||||||
safe_json_add_int_from_string(item, "id", dbpli->id);
|
safe_json_add_int_from_string(item, "id", dbpli->id);
|
||||||
safe_json_add_string(item, "name", dbpli->title);
|
safe_json_add_string(item, "name", dbpli->title);
|
||||||
safe_json_add_string(item, "path", dbpli->path);
|
safe_json_add_string(item, "path", dbpli->path);
|
||||||
|
safe_json_add_string(item, "parent_id", dbpli->parent_id);
|
||||||
ret = safe_atoi32(dbpli->type, &intval);
|
ret = safe_atoi32(dbpli->type, &intval);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
safe_json_add_string(item, "type", db_pl_type_label(intval));
|
||||||
json_object_object_add(item, "smart_playlist", json_object_new_boolean(intval == PL_SMART));
|
json_object_object_add(item, "smart_playlist", json_object_new_boolean(intval == PL_SMART));
|
||||||
|
json_object_object_add(item, "folder", json_object_new_boolean(intval == PL_FOLDER));
|
||||||
|
}
|
||||||
|
|
||||||
ret = snprintf(uri, sizeof(uri), "%s:%s:%s", "library", "playlist", dbpli->id);
|
ret = snprintf(uri, sizeof(uri), "%s:%s:%s", "library", "playlist", dbpli->id);
|
||||||
if (ret < sizeof(uri))
|
if (ret < sizeof(uri))
|
||||||
@ -3265,6 +3270,65 @@ jsonapi_reply_library_playlist_tracks(struct httpd_request *hreq)
|
|||||||
return HTTP_OK;
|
return HTTP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
jsonapi_reply_library_playlist_playlists(struct httpd_request *hreq)
|
||||||
|
{
|
||||||
|
struct query_params query_params;
|
||||||
|
json_object *reply;
|
||||||
|
json_object *items;
|
||||||
|
int playlist_id;
|
||||||
|
int total;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!is_modified(hreq->req, DB_ADMIN_DB_MODIFIED))
|
||||||
|
return HTTP_NOTMODIFIED;
|
||||||
|
|
||||||
|
|
||||||
|
ret = safe_atoi32(hreq->uri_parsed->path_parts[3], &playlist_id);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_WEB, "No valid playlist id given '%s'\n", hreq->uri_parsed->path);
|
||||||
|
|
||||||
|
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_PL;
|
||||||
|
query_params.sort = S_PLAYLIST;
|
||||||
|
query_params.filter = db_mprintf("f.parent_id = %d AND (f.type = %d OR f.type = %d OR f.type = %d)",
|
||||||
|
playlist_id, PL_PLAIN, PL_SMART, PL_FOLDER);
|
||||||
|
|
||||||
|
ret = fetch_playlists(&query_params, items, &total);
|
||||||
|
if (ret < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
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, "playlist tracks: Couldn't add tracks to response buffer.\n");
|
||||||
|
|
||||||
|
error:
|
||||||
|
free_query_params(&query_params, 1);
|
||||||
|
jparse_free(reply);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return HTTP_INTERNAL;
|
||||||
|
|
||||||
|
return HTTP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
jsonapi_reply_queue_save(struct httpd_request *hreq)
|
jsonapi_reply_queue_save(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
@ -3902,6 +3966,7 @@ static struct httpd_uri_map adm_handlers[] =
|
|||||||
{ EVHTTP_REQ_GET, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlist_tracks },
|
{ EVHTTP_REQ_GET, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlist_tracks },
|
||||||
// { EVHTTP_REQ_POST, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlists_tracks },
|
// { EVHTTP_REQ_POST, "^/api/library/playlists/[[:digit:]]+/tracks$", jsonapi_reply_library_playlists_tracks },
|
||||||
// { EVHTTP_REQ_DELETE, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_tracks },
|
// { EVHTTP_REQ_DELETE, "^/api/library/playlists/[[:digit:]]+$", jsonapi_reply_library_playlist_tracks },
|
||||||
|
{ EVHTTP_REQ_GET, "^/api/library/playlists/[[:digit:]]+/playlists", jsonapi_reply_library_playlist_playlists },
|
||||||
{ EVHTTP_REQ_GET, "^/api/library/artists$", jsonapi_reply_library_artists },
|
{ EVHTTP_REQ_GET, "^/api/library/artists$", jsonapi_reply_library_artists },
|
||||||
{ EVHTTP_REQ_GET, "^/api/library/artists/[[:digit:]]+$", jsonapi_reply_library_artist },
|
{ EVHTTP_REQ_GET, "^/api/library/artists/[[:digit:]]+$", jsonapi_reply_library_artist },
|
||||||
{ EVHTTP_REQ_GET, "^/api/library/artists/[[:digit:]]+/albums$", jsonapi_reply_library_artist_albums },
|
{ EVHTTP_REQ_GET, "^/api/library/artists/[[:digit:]]+/albums$", jsonapi_reply_library_artist_albums },
|
||||||
|
Loading…
Reference in New Issue
Block a user