From 42cbd721fda04884294d83c06b9a17684ab71788 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 24 Nov 2018 09:23:32 +0100 Subject: [PATCH 1/9] [db] Minor version upgrade: new column "path" in directories table --- src/db.c | 11 ++++++----- src/db.h | 1 + src/db_init.c | 19 ++++++++++--------- src/db_init.h | 2 +- src/db_upgrade.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/db.c b/src/db.c index ac925ec1..b627736d 100644 --- a/src/db.c +++ b/src/db.c @@ -3944,6 +3944,7 @@ db_directory_enum_fetch(struct directory_enum *de, struct directory_info *di) disabled = sqlite3_column_int64(de->stmt, 3); di->disabled = (disabled != 0); di->parent_id = sqlite3_column_int(de->stmt, 4); + di->path = (char *)sqlite3_column_text(de->stmt, 5); return 0; } @@ -3961,8 +3962,8 @@ db_directory_enum_end(struct directory_enum *de) static int db_directory_add(struct directory_info *di, int *id) { -#define QADD_TMPL "INSERT INTO directories (virtual_path, db_timestamp, disabled, parent_id)" \ - " VALUES (TRIM(%Q), %d, %d, %d);" +#define QADD_TMPL "INSERT INTO directories (virtual_path, db_timestamp, disabled, parent_id, path)" \ + " VALUES (TRIM(%Q), %d, %d, %d, TRIM(%Q));" char *query; char *errmsg; @@ -3977,7 +3978,7 @@ db_directory_add(struct directory_info *di, int *id) DPRINTF(E_LOG, L_DB, "Directory name ends with space: '%s'\n", di->virtual_path); } - query = sqlite3_mprintf(QADD_TMPL, di->virtual_path, di->db_timestamp, di->disabled, di->parent_id); + query = sqlite3_mprintf(QADD_TMPL, di->virtual_path, di->db_timestamp, di->disabled, di->parent_id, di->path); if (!query) { @@ -4016,14 +4017,14 @@ db_directory_add(struct directory_info *di, int *id) static int db_directory_update(struct directory_info *di) { -#define QADD_TMPL "UPDATE directories SET virtual_path = TRIM(%Q), db_timestamp = %d, disabled = %d, parent_id = %d" \ +#define QADD_TMPL "UPDATE directories SET virtual_path = TRIM(%Q), db_timestamp = %d, disabled = %d, parent_id = %d, path = TRIM(%Q)" \ " WHERE id = %d;" char *query; char *errmsg; int ret; /* Add */ - query = sqlite3_mprintf(QADD_TMPL, di->virtual_path, di->db_timestamp, di->disabled, di->parent_id, di->id); + query = sqlite3_mprintf(QADD_TMPL, di->virtual_path, di->db_timestamp, di->disabled, di->parent_id, di->path, di->id); if (!query) { diff --git a/src/db.h b/src/db.h index 4f3b1918..f7fd3672 100644 --- a/src/db.h +++ b/src/db.h @@ -412,6 +412,7 @@ enum directory_ids { struct directory_info { uint32_t id; char *virtual_path; + char *path; uint32_t db_timestamp; uint32_t disabled; uint32_t parent_id; diff --git a/src/db_init.c b/src/db_init.c index 91414743..eba7116d 100644 --- a/src/db_init.c +++ b/src/db_init.c @@ -161,7 +161,8 @@ " virtual_path VARCHAR(4096) NOT NULL," \ " db_timestamp INTEGER DEFAULT 0," \ " disabled INTEGER DEFAULT 0," \ - " parent_id INTEGER DEFAULT 0" \ + " parent_id INTEGER DEFAULT 0," \ + " path VARCHAR(4096) DEFAULT NULL" \ ");" #define T_QUEUE \ @@ -239,17 +240,17 @@ #define Q_DIR1 \ - "INSERT INTO directories (id, virtual_path, db_timestamp, disabled, parent_id)" \ - " VALUES (1, '/', 0, 0, 0);" + "INSERT INTO directories (id, virtual_path, db_timestamp, disabled, parent_id, path)" \ + " VALUES (1, '/', 0, 0, 0, NULL);" #define Q_DIR2 \ - "INSERT INTO directories (id, virtual_path, db_timestamp, disabled, parent_id)" \ - " VALUES (2, '/file:', 0, 0, 1);" + "INSERT INTO directories (id, virtual_path, db_timestamp, disabled, parent_id, path)" \ + " VALUES (2, '/file:', 0, 0, 1, '/');" #define Q_DIR3 \ - "INSERT INTO directories (id, virtual_path, db_timestamp, disabled, parent_id)" \ - " VALUES (3, '/http:', 0, 0, 1);" + "INSERT INTO directories (id, virtual_path, db_timestamp, disabled, parent_id, path)" \ + " VALUES (3, '/http:', 0, 0, 1, NULL);" #define Q_DIR4 \ - "INSERT INTO directories (id, virtual_path, db_timestamp, disabled, parent_id)" \ - " VALUES (4, '/spotify:', 0, 4294967296, 1);" + "INSERT INTO directories (id, virtual_path, db_timestamp, disabled, parent_id, path)" \ + " VALUES (4, '/spotify:', 0, 4294967296, 1, NULL);" #define Q_QUEUE_VERSION \ "INSERT INTO admin (key, value) VALUES ('queue_version', '0');" diff --git a/src/db_init.h b/src/db_init.h index 69f54572..ec3f0217 100644 --- a/src/db_init.h +++ b/src/db_init.h @@ -26,7 +26,7 @@ * is a major upgrade. In other words minor version upgrades permit downgrading * forked-daapd after the database was upgraded. */ #define SCHEMA_VERSION_MAJOR 19 -#define SCHEMA_VERSION_MINOR 11 +#define SCHEMA_VERSION_MINOR 12 int db_init_indices(sqlite3 *hdl); diff --git a/src/db_upgrade.c b/src/db_upgrade.c index a84cd053..baad11a9 100644 --- a/src/db_upgrade.c +++ b/src/db_upgrade.c @@ -1698,6 +1698,27 @@ static const struct db_upgrade_query db_upgrade_v1911_queries[] = }; +#define U_V1912_ALTER_DIRECTORIES_ADD_PATH \ + "ALTER TABLE directories ADD COLUMN path VARCHAR(4096) DEFAULT NULL;" + +#define U_V1912_UPDATE_FILE_DIRECTORIES_PATH \ + "UPDATE directories SET path = SUBSTR(path, 7) WHERE virtual_path like '/file:/%';" +#define U_V1912_UPDATE_FILE_ROOT_PATH \ + "UPDATE directories SET path = '/' WHERE virtual_path = '/file:';" + +#define U_V1912_SCVER_MINOR \ + "UPDATE admin SET value = '12' WHERE key = 'schema_version_minor';" + +static const struct db_upgrade_query db_upgrade_v1912_queries[] = + { + { U_V1912_ALTER_DIRECTORIES_ADD_PATH, "alter table directories add column path" }, + { U_V1912_UPDATE_FILE_DIRECTORIES_PATH, "set paths for '/file:' directories" }, + { U_V1912_UPDATE_FILE_ROOT_PATH, "set path for '/file:' directory" }, + + { U_V1912_SCVER_MINOR, "set schema_version_minor to 12" }, + }; + + int db_upgrade(sqlite3 *hdl, int db_ver) { @@ -1884,6 +1905,14 @@ db_upgrade(sqlite3 *hdl, int db_ver) ret = db_generic_upgrade(hdl, db_upgrade_v1911_queries, ARRAY_SIZE(db_upgrade_v1911_queries)); if (ret < 0) return -1; + + /* FALLTHROUGH */ + + case 1911: + ret = db_generic_upgrade(hdl, db_upgrade_v1912_queries, ARRAY_SIZE(db_upgrade_v1912_queries)); + if (ret < 0) + return -1; + break; default: From dc020cc3aced26f520608d0ee1870513c83857b3 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 24 Nov 2018 09:44:18 +0100 Subject: [PATCH 2/9] [db/filescanner/spotify] Set directory path --- src/db.c | 3 ++- src/db.h | 2 +- src/library/filescanner.c | 4 ++-- src/spotify_webapi.c | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/db.c b/src/db.c index b627736d..f3774d4a 100644 --- a/src/db.c +++ b/src/db.c @@ -4054,7 +4054,7 @@ db_directory_update(struct directory_info *di) } int -db_directory_addorupdate(char *virtual_path, int disabled, int parent_id) +db_directory_addorupdate(char *virtual_path, char *path, int disabled, int parent_id) { struct directory_info di; int id; @@ -4065,6 +4065,7 @@ db_directory_addorupdate(char *virtual_path, int disabled, int parent_id) di.id = id; di.parent_id = parent_id; di.virtual_path = virtual_path; + di.path = path; di.disabled = disabled; di.db_timestamp = (uint64_t)time(NULL); diff --git a/src/db.h b/src/db.h index f7fd3672..75722213 100644 --- a/src/db.h +++ b/src/db.h @@ -711,7 +711,7 @@ void db_directory_enum_end(struct directory_enum *de); int -db_directory_addorupdate(char *virtual_path, int disabled, int parent_id); +db_directory_addorupdate(char *virtual_path, char *path, int disabled, int parent_id); void db_directory_ping_bymatch(char *virtual_path); diff --git a/src/library/filescanner.c b/src/library/filescanner.c index 18d4840d..8a03ff15 100644 --- a/src/library/filescanner.c +++ b/src/library/filescanner.c @@ -749,7 +749,7 @@ process_directory(char *path, int parent_id, int flags) if (ret < 0) return; - dir_id = db_directory_addorupdate(virtual_path, 0, parent_id); + dir_id = db_directory_addorupdate(virtual_path, path, 0, parent_id); if (dir_id <= 0) { DPRINTF(E_LOG, L_SCAN, "Insert or update of directory failed '%s'\n", virtual_path); @@ -876,7 +876,7 @@ process_parent_directories(char *path) if (ret < 0) return 0; - dir_id = db_directory_addorupdate(virtual_path, 0, dir_id); + dir_id = db_directory_addorupdate(virtual_path, buf, 0, dir_id); if (dir_id <= 0) { DPRINTF(E_LOG, L_SCAN, "Insert or update of directory failed '%s'\n", virtual_path); diff --git a/src/spotify_webapi.c b/src/spotify_webapi.c index 9e22a033..d9021fcb 100644 --- a/src/spotify_webapi.c +++ b/src/spotify_webapi.c @@ -1250,7 +1250,7 @@ prepare_directories(const char *artist, const char *album) DPRINTF(E_LOG, L_SPOTIFY, "Virtual path exceeds PATH_MAX (/spotify:/%s)\n", artist); return -1; } - dir_id = db_directory_addorupdate(virtual_path, 0, DIR_SPOTIFY); + dir_id = db_directory_addorupdate(virtual_path, NULL, 0, DIR_SPOTIFY); if (dir_id <= 0) { DPRINTF(E_LOG, L_SPOTIFY, "Could not add or update directory '%s'\n", virtual_path); @@ -1262,7 +1262,7 @@ prepare_directories(const char *artist, const char *album) DPRINTF(E_LOG, L_SPOTIFY, "Virtual path exceeds PATH_MAX (/spotify:/%s/%s)\n", artist, album); return -1; } - dir_id = db_directory_addorupdate(virtual_path, 0, dir_id); + dir_id = db_directory_addorupdate(virtual_path, NULL, 0, dir_id); if (dir_id <= 0) { DPRINTF(E_LOG, L_SPOTIFY, "Could not add or update directory '%s'\n", virtual_path); From 070379edef2509712878434e7d35b235c7f5bd82 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 24 Nov 2018 12:14:41 +0100 Subject: [PATCH 3/9] [db] Fetch directory id by path; use const for path and virtual_path --- src/db.c | 26 +++++++++++++++++++++++++- src/db.h | 5 ++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/db.c b/src/db.c index f3774d4a..c675bcc5 100644 --- a/src/db.c +++ b/src/db.c @@ -3850,7 +3850,7 @@ db_group_persistentid_byid(int id, int64_t *persistentid) /* Directories */ int -db_directory_id_byvirtualpath(char *virtual_path) +db_directory_id_byvirtualpath(const char *virtual_path) { #define Q_TMPL "SELECT d.id FROM directories d WHERE d.virtual_path = '%q';" char *query; @@ -3873,6 +3873,30 @@ db_directory_id_byvirtualpath(char *virtual_path) #undef Q_TMPL } +int +db_directory_id_bypath(const char *path) +{ +#define Q_TMPL "SELECT d.id FROM directories d WHERE d.path = '%q';" + char *query; + int ret; + + query = sqlite3_mprintf(Q_TMPL, path); + if (!query) + { + DPRINTF(E_LOG, L_DB, "Out of memory for query string\n"); + + return 0; + } + + ret = db_file_id_byquery(query); + + sqlite3_free(query); + + return ret; + +#undef Q_TMPL +} + int db_directory_enum_start(struct directory_enum *de) { diff --git a/src/db.h b/src/db.h index 75722213..1ed9dc92 100644 --- a/src/db.h +++ b/src/db.h @@ -699,7 +699,10 @@ db_group_persistentid_byid(int id, int64_t *persistentid); /* Directories */ int -db_directory_id_byvirtualpath(char *virtual_path); +db_directory_id_byvirtualpath(const char *virtual_path); + +int +db_directory_id_bypath(const char *path); int db_directory_enum_start(struct directory_enum *de); From e6dc2a5845ce9bdb07cdcbde305e064b9d1ce2e7 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 24 Nov 2018 12:15:31 +0100 Subject: [PATCH 4/9] [jsonapi] Add new endpoint "api/library/files" This endpoint allows traversing the directory tree of the local library. --- src/httpd_jsonapi.c | 152 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index ae0e84ce..91d0cfa7 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -266,6 +266,24 @@ genre_to_json(const char *genre) return item; } +static json_object * +directory_to_json(struct directory_info *directory_info) +{ + json_object *item; + + if (directory_info == NULL) + { + return NULL; + } + + item = json_object_new_object(); + safe_json_add_string(item, "path", directory_info->path); +// json_object_object_add(item, "id", json_object_new_int(directory_info->id)); +// json_object_object_add(item, "parent_id", json_object_new_int(directory_info->parent_id)); + + return item; +} + static int fetch_tracks(struct query_params *query_params, json_object *items, int *total) @@ -529,6 +547,38 @@ fetch_genres(struct query_params *query_params, json_object *items, int *total) return ret; } +static int +fetch_directories(int parent_id, json_object *items) +{ + json_object *item; + int ret; + struct directory_info subdir; + struct directory_enum dir_enum; + + memset(&dir_enum, 0, sizeof(struct directory_enum)); + dir_enum.parent_id = parent_id; + ret = db_directory_enum_start(&dir_enum); + if (ret < 0) + goto error; + + while ((ret = db_directory_enum_fetch(&dir_enum, &subdir)) == 0 && subdir.id > 0) + { + item = directory_to_json(&subdir); + if (!item) + { + ret = -1; + goto error; + } + + json_object_array_add(items, item); + } + + error: + db_directory_enum_end(&dir_enum); + + return ret; +} + static int query_params_limit_set(struct query_params *query_params, struct httpd_request *hreq) @@ -2609,6 +2659,107 @@ jsonapi_reply_library_count(struct httpd_request *hreq) return HTTP_OK; } +static int +jsonapi_reply_library_files(struct httpd_request *hreq) +{ + const char *param; + int directory_id; + json_object *reply; + json_object *directories; + struct query_params query_params; + json_object *tracks; + json_object *tracks_items; + json_object *playlists; + json_object *playlists_items; + int total; + int ret; + + param = evhttp_find_header(hreq->query, "directory"); + + directory_id = DIR_FILE; + if (param) + { + directory_id = db_directory_id_bypath(param); + if (directory_id <= 0) + return HTTP_INTERNAL; + } + + reply = json_object_new_object(); + + // Add sub directories to response + directories = json_object_new_array(); + json_object_object_add(reply, "directories", directories); + + ret = fetch_directories(directory_id, directories); + if (ret < 0) + { + goto error; + } + + // Add tracks to response + tracks = json_object_new_object(); + json_object_object_add(reply, "tracks", tracks); + tracks_items = json_object_new_array(); + json_object_object_add(tracks, "items", tracks_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_ITEMS; + query_params.sort = S_NAME; + query_params.filter = db_mprintf("(f.directory_id = %d)", directory_id); + + ret = fetch_tracks(&query_params, tracks_items, &total); + free(query_params.filter); + + if (ret < 0) + goto error; + + json_object_object_add(tracks, "total", json_object_new_int(total)); + json_object_object_add(tracks, "offset", json_object_new_int(query_params.offset)); + json_object_object_add(tracks, "limit", json_object_new_int(query_params.limit)); + + // Add playlists + playlists = json_object_new_object(); + json_object_object_add(reply, "playlists", playlists); + playlists_items = json_object_new_array(); + json_object_object_add(playlists, "items", playlists_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.directory_id = %d)", directory_id); + + ret = fetch_playlists(&query_params, playlists_items, &total); + free(query_params.filter); + + if (ret < 0) + goto error; + + json_object_object_add(playlists, "total", json_object_new_int(total)); + json_object_object_add(playlists, "offset", json_object_new_int(query_params.offset)); + json_object_object_add(playlists, "limit", json_object_new_int(query_params.limit)); + + // Build JSON response + 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 directories to response buffer.\n"); + + error: + jparse_free(reply); + + if (ret < 0) + return HTTP_INTERNAL; + + return HTTP_OK; +} + static int search_tracks(json_object *reply, struct httpd_request *hreq, const char *param_query, struct smartpl *smartpl_expression, enum media_kind media_kind) { @@ -2977,6 +3128,7 @@ static struct httpd_uri_map adm_handlers[] = { 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/library/files$", jsonapi_reply_library_files }, { EVHTTP_REQ_GET, "^/api/search$", jsonapi_reply_search }, From ccb0090e53dbcf9e4afdbd90ba397291254ee388 Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 24 Nov 2018 12:43:28 +0100 Subject: [PATCH 5/9] [README] Add new endpoint "api/library/files" to JSON API README --- README_JSON_API.md | 110 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/README_JSON_API.md b/README_JSON_API.md index 0688526c..fb1ec68f 100644 --- a/README_JSON_API.md +++ b/README_JSON_API.md @@ -673,6 +673,7 @@ curl -X PUT "http://localhost:3689/api/queue/items/2" | 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 | +| GET | [/api/library/files](#list-local-directories) | Get list of directories in the local library | | GET | [/api/update](#trigger-rescan) | Trigger a library rescan | @@ -1205,7 +1206,7 @@ curl -X GET "http://localhost:3689/api/library/albums/1/tracks" ``` -### list genres +### List genres Get list of genres @@ -1377,6 +1378,105 @@ curl -X GET "http://localhost:3689/api/library/count?expression=data_kind+is+fil ``` +### List local directories + +List the local directories and the directory contents (tracks and playlists) + + +**Endpoint** + +```http +GET /api/library/files +``` + +**Query parameters** + +| Parameter | Value | +| --------------- | ----------------------------------------------------------- | +| directory | *(Optional)* A path to a directory in your local library. | + +**Response** + +| Key | Type | Value | +| --------------- | -------- | ----------------------------------------- | +| directories | array | Array of [`directory`](#directory-object) objects containing the sub directories | +| tracks | object | [`paging`](#paging-object) object containing [`track`](#track-object) objects that matches the `directory` | +| playlists | object | [`paging`](#paging-object) object containing [`playlist`](#playlist-object) objects that matches the `directory` | + + +**Example** + +```shell +curl -X GET "http://localhost:3689/api/library/files?directory=/music/srv" +``` + +```json +{ + "directories": [ + { + "path": "/music/srv/Audiobooks" + }, + { + "path": "/music/srv/Music" + }, + { + "path": "/music/srv/Playlists" + }, + { + "path": "/music/srv/Podcasts" + } + ], + "tracks": { + "items": [ + { + "id": 1, + "title": "input.pipe", + "artist": "Unknown artist", + "artist_sort": "Unknown artist", + "album": "Unknown album", + "album_sort": "Unknown album", + "album_id": "4201163758598356043", + "album_artist": "Unknown artist", + "album_artist_sort": "Unknown artist", + "album_artist_id": "4187901437947843388", + "genre": "Unknown genre", + "year": 0, + "track_number": 0, + "disc_number": 0, + "length_ms": 0, + "play_count": 0, + "skip_count": 0, + "time_added": "2018-11-24T08:41:35Z", + "seek_ms": 0, + "media_kind": "music", + "data_kind": "pipe", + "path": "/music/srv/input.pipe", + "uri": "library:track:1", + "artwork_url": "/artwork/item/1" + } + ], + "total": 1, + "offset": 0, + "limit": -1 + }, + "playlists": { + "items": [ + { + "id": 8, + "name": "radio", + "path": "/music/srv/radio.m3u", + "smart_playlist": true, + "uri": "library:playlist:8" + } + ], + "total": 1, + "offset": 0, + "limit": -1 + } +} +``` + + ### Trigger rescan Trigger a library rescan @@ -1817,6 +1917,12 @@ curl --include \ | name | string | Name of genre | +### `directory` object + +| Key | Type | Value | +| --------------- | -------- | ----------------------------------------- | +| path | string | Directory path | + ### Artwork urls @@ -1825,4 +1931,4 @@ Absolute artwork urls are pointing to external artwork images (e. g. for radio s It is possible to add the query parameters `maxwidth` and/or `maxheight` to relative artwork urls, in order to get a smaller image (forked-daapd only scales down never up). -Note that even if a relative artwork url attribute is present, it is not guaranteed to exist. \ No newline at end of file +Note that even if a relative artwork url attribute is present, it is not guaranteed to exist. From b558e3498d262b4263471b771a1309b7bcc9668b Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 15 Dec 2018 09:01:36 +0100 Subject: [PATCH 6/9] [jsonapi] Add rating and title_sort to track object --- src/httpd_jsonapi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index 91d0cfa7..6bb7f611 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -182,6 +182,7 @@ track_to_json(struct db_media_file_info *dbmfi) safe_json_add_int_from_string(item, "id", dbmfi->id); safe_json_add_string(item, "title", dbmfi->title); + safe_json_add_string(item, "title_sort", dbmfi->title_sort); safe_json_add_string(item, "artist", dbmfi->artist); safe_json_add_string(item, "artist_sort", dbmfi->artist_sort); safe_json_add_string(item, "album", dbmfi->album); @@ -197,6 +198,7 @@ track_to_json(struct db_media_file_info *dbmfi) safe_json_add_int_from_string(item, "disc_number", dbmfi->disc); safe_json_add_int_from_string(item, "length_ms", dbmfi->song_length); + safe_json_add_int_from_string(item, "rating", dbmfi->rating); safe_json_add_int_from_string(item, "play_count", dbmfi->play_count); safe_json_add_int_from_string(item, "skip_count", dbmfi->skip_count); safe_json_add_time_from_string(item, "time_played", dbmfi->time_played, true); From 21bfd0645b81b6cbfb0c4893555752d8903ef20a Mon Sep 17 00:00:00 2001 From: chme Date: Sat, 15 Dec 2018 09:02:02 +0100 Subject: [PATCH 7/9] [README] Add missing documentation for track object fields --- README_JSON_API.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README_JSON_API.md b/README_JSON_API.md index fb1ec68f..fd24e847 100644 --- a/README_JSON_API.md +++ b/README_JSON_API.md @@ -1874,6 +1874,7 @@ curl --include \ | ------------------ | -------- | ----------------------------------------- | | id | string | Track id | | title | string | Title | +| title_sort | string | Sort title | | artist | string | Track artist name | | artist_sort | string | Track artist sort name | | album | string | Album name | @@ -1888,8 +1889,11 @@ curl --include \ | track_number | integer | Track number | | disc_number | integer | Disc number | | length_ms | integer | Track length in milliseconds | +| rating | integer | Track rating (ranges from 0 to 100) | | play_count | integer | How many times the track was played | +| skip_count | integer | How many times the track was skipped | | time_played | string | Timestamp in `ISO 8601` format | +| time_skipped | string | Timestamp in `ISO 8601` format | | time_added | string | Timestamp in `ISO 8601` format | | date_released | string | Date in the format `yyyy-mm-dd` | | seek_ms | integer | Resume point in milliseconds (available only for podcasts and audiobooks) | From 2b7ff60771f4a9ba8d769349335175044a1e05f2 Mon Sep 17 00:00:00 2001 From: chme Date: Sun, 2 Dec 2018 07:14:26 +0100 Subject: [PATCH 8/9] [jsonapi] Fix playing from position if player state is shuffled --- src/httpd_jsonapi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index 6bb7f611..43447571 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -1327,6 +1327,7 @@ static int play_item_at_position(const char *param) { uint32_t position; + struct player_status status; struct db_queue_item *queue_item; int ret; @@ -1338,7 +1339,9 @@ play_item_at_position(const char *param) return HTTP_BADREQUEST; } - queue_item = db_queue_fetch_bypos(position, 0); + player_get_status(&status); + + queue_item = db_queue_fetch_bypos(position, status.shuffle); if (!queue_item) { DPRINTF(E_LOG, L_WEB, "No queue item at position '%d'\n", position); From dcbe5036865305d6ed7808eb60f4166e34fe0c8d Mon Sep 17 00:00:00 2001 From: chme Date: Mon, 17 Dec 2018 19:03:34 +0100 Subject: [PATCH 9/9] [jsonapi] Return number of items added in queue/items/add endpoint response --- README_JSON_API.md | 13 ++++++++++++- src/httpd_jsonapi.c | 12 +++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README_JSON_API.md b/README_JSON_API.md index fd24e847..6a62cf88 100644 --- a/README_JSON_API.md +++ b/README_JSON_API.md @@ -587,7 +587,12 @@ POST /api/queue/items/add **Response** -On success returns the HTTP `204 No Content` success status response code. +On success returns the HTTP `200 OK` success status response code. + +| Key | Type | Value | +| --------------- | -------- | ----------------------------------------- | +| count | integer | number of tracks added to the queue | + **Example** @@ -595,6 +600,12 @@ On success returns the HTTP `204 No Content` success status response code. curl -X POST "http://localhost:3689/api/queue/items/add?uris=library:playlist:68,library:artist:2932599850102967727" ``` +```json +{ + "count": 42 +} +``` + ### Moving a queue item diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index 43447571..71260f84 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -1745,6 +1745,7 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq) const char *id; int pos = -1; int count = 0; + json_object *reply; int ret = 0; @@ -1814,10 +1815,19 @@ jsonapi_reply_queue_tracks_add(struct httpd_request *hreq) free(uris); + if (ret == 0) + { + reply = json_object_new_object(); + json_object_object_add(reply, "count", json_object_new_int(count)); + + ret = evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(reply)); + jparse_free(reply); + } + if (ret < 0) return HTTP_INTERNAL; - return HTTP_NOCONTENT; + return HTTP_OK; } static int