From d7086cab000bc5ac1389554eed7e99a84218ea1a Mon Sep 17 00:00:00 2001 From: chme Date: Fri, 24 Dec 2021 14:02:37 +0100 Subject: [PATCH] [db,jsonapi] Additional meta data for browse queries --- src/db.c | 79 ++++++++++++++++++++++++++++++++++++++++++++- src/db.h | 21 ++++++++++++ src/httpd_jsonapi.c | 38 ++++++++++++++++++---- 3 files changed, 130 insertions(+), 8 deletions(-) diff --git a/src/db.c b/src/db.c index 41596213..2cadf011 100644 --- a/src/db.c +++ b/src/db.c @@ -420,6 +420,27 @@ static const ssize_t dbgri_cols_map[] = dbgri_offsetof(seek), }; +/* This list must be kept in sync with + * - the order of fields in the Q_BROWSE_INFO query + * - the name of the fields in struct db_browse_info + */ +static const ssize_t dbbi_cols_map[] = + { + dbbi_offsetof(itemname), + dbbi_offsetof(itemname_sort), + dbbi_offsetof(track_count), + dbbi_offsetof(album_count), + dbbi_offsetof(artist_count), + dbbi_offsetof(song_length), + dbbi_offsetof(data_kind), + dbbi_offsetof(media_kind), + dbbi_offsetof(year), + dbbi_offsetof(date_released), + dbbi_offsetof(time_added), + dbbi_offsetof(time_played), + dbbi_offsetof(seek), + }; + /* This list must be kept in sync with * - qi_cols_map */ @@ -2188,7 +2209,10 @@ db_build_query_browse(struct query_params *qp, struct query_clause *qc) where = browse_clause[qp->type & ~Q_F_BROWSE].where; count = sqlite3_mprintf("SELECT COUNT(*) FROM (SELECT %s FROM files f %s AND %s != '' %s);", select, qc->where, where, qc->group); - query = sqlite3_mprintf("SELECT %s FROM files f %s AND %s != '' %s %s %s;", select, qc->where, where, qc->group, qc->order, qc->index); + query = sqlite3_mprintf("SELECT %s, COUNT(f.id) as track_count, COUNT(DISTINCT f.songalbumid) as album_count, COUNT(DISTINCT f.songartistid) as artist_count, " + " SUM(f.song_length), MIN(f.data_kind), MIN(f.media_kind), MAX(f.year), MAX(f.date_released), " + " MAX(f.time_added), MAX(f.time_played), MAX(f.seek) FROM files f %s AND %s != '' %s %s %s;", + select, qc->where, where, qc->group, qc->order, qc->index); return db_build_query_check(qp, count, query); } @@ -2522,6 +2546,59 @@ db_query_fetch_group(struct query_params *qp, struct db_group_info *dbgri) return 0; } +int +db_query_fetch_browse(struct query_params *qp, struct db_browse_info *dbbi) +{ + int ncols; + char **strcol; + int i; + int ret; + + memset(dbbi, 0, sizeof(struct db_browse_info)); + + if (!qp->stmt) + { + DPRINTF(E_LOG, L_DB, "Query not started!\n"); + return -1; + } + + if (!(qp->type & Q_F_BROWSE)) + { + DPRINTF(E_LOG, L_DB, "Not a browse query!\n"); + return -1; + } + + ret = db_blocking_step(qp->stmt); + if (ret == SQLITE_DONE) + { + DPRINTF(E_DBG, L_DB, "End of query results\n"); + return 1; + } + else if (ret != SQLITE_ROW) + { + DPRINTF(E_LOG, L_DB, "Could not step: %s\n", sqlite3_errmsg(hdl)); + return -1; + } + + ncols = sqlite3_column_count(qp->stmt); + + // We allow more cols in db than in map because the db may be a future schema + if (ncols < ARRAY_SIZE(dbbi_cols_map)) + { + DPRINTF(E_LOG, L_DB, "BUG: database has fewer columns (%d) than dbbi column map (%u)\n", ncols, ARRAY_SIZE(dbbi_cols_map)); + return -1; + } + + for (i = 0; i < ARRAY_SIZE(dbbi_cols_map); i++) + { + strcol = (char **) ((char *)dbbi + dbbi_cols_map[i]); + + *strcol = (char *)sqlite3_column_text(qp->stmt, i); + } + + return 0; +} + int db_query_fetch_count(struct query_params *qp, struct filecount_info *fci) { diff --git a/src/db.h b/src/db.h index dda94c1e..eceb9db6 100644 --- a/src/db.h +++ b/src/db.h @@ -409,6 +409,24 @@ struct db_media_file_info { #define dbmfi_offsetof(field) offsetof(struct db_media_file_info, field) +struct db_browse_info { + char *itemname; + char *itemname_sort; + char *track_count; + char *album_count; + char *artist_count; + char *song_length; + char *data_kind; + char *media_kind; + char *year; + char *date_released; + char *time_added; + char *time_played; + char *seek; +}; + +#define dbbi_offsetof(field) offsetof(struct db_browse_info, field) + enum strip_type { STRIP_NONE, STRIP_PATH, @@ -593,6 +611,9 @@ db_query_fetch_pl(struct query_params *qp, struct db_playlist_info *dbpli); int db_query_fetch_group(struct query_params *qp, struct db_group_info *dbgri); +int +db_query_fetch_browse(struct query_params *qp, struct db_browse_info *dbbi); + int db_query_fetch_count(struct query_params *qp, struct filecount_info *fci); diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index ea00e4f3..9e4f99b4 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -388,17 +388,42 @@ playlist_to_json(struct db_playlist_info *dbpli) } static json_object * -genre_to_json(const char *genre) +browse_info_to_json(struct db_browse_info *dbbi) { json_object *item; + int intval; + int ret; - if (genre == NULL) + if (dbbi == NULL) { return NULL; } item = json_object_new_object(); - safe_json_add_string(item, "name", genre); + safe_json_add_string(item, "name", dbbi->itemname); + safe_json_add_string(item, "name_sort", dbbi->itemname_sort); + safe_json_add_int_from_string(item, "track_count", dbbi->track_count); + safe_json_add_int_from_string(item, "album_count", dbbi->album_count); + safe_json_add_int_from_string(item, "artist_count", dbbi->artist_count); + safe_json_add_int_from_string(item, "length_ms", dbbi->song_length); + + safe_json_add_time_from_string(item, "time_played", dbbi->time_played); + safe_json_add_time_from_string(item, "time_added", dbbi->time_added); + + ret = safe_atoi32(dbbi->seek, &intval); + if (ret == 0) + json_object_object_add(item, "in_progress", json_object_new_boolean(intval > 0)); + + ret = safe_atoi32(dbbi->media_kind, &intval); + if (ret == 0) + safe_json_add_string(item, "media_kind", db_media_kind_label(intval)); + + ret = safe_atoi32(dbbi->data_kind, &intval); + if (ret == 0) + safe_json_add_string(item, "data_kind", db_data_kind_label(intval)); + + safe_json_add_date_from_string(item, "date_released", dbbi->date_released); + safe_json_add_int_from_string(item, "year", dbbi->year); return item; } @@ -662,18 +687,17 @@ fetch_playlist(bool *notfound, uint32_t playlist_id) static int fetch_genres(struct query_params *query_params, json_object *items, int *total) { + struct db_browse_info dbbi; 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)) + while (((ret = db_query_fetch_browse(query_params, &dbbi)) == 0) && (dbbi.track_count)) { - item = genre_to_json(genre); + item = browse_info_to_json(&dbbi); if (!item) { ret = -1;