diff --git a/README_JSON_API.md b/README_JSON_API.md index 5d8aceac..2d454bab 100644 --- a/README_JSON_API.md +++ b/README_JSON_API.md @@ -1724,6 +1724,7 @@ curl --include \ | data_kind | string | Data type of this track: `file`, `url`, `spotify`, `pipe` | | path | string | Path | | uri | string | Resource identifier | +| artwork_url | string | *(optional)* [Artwork url](#artwork-urls) | ### `playlist` object @@ -1748,6 +1749,7 @@ curl --include \ | track_count | integer | Number of tracks | | length_ms | integer | Total length of tracks in milliseconds | | uri | string | Resource identifier | +| artwork_url | string | *(optional)* [Artwork url](#artwork-urls) | ### `album` object @@ -1762,6 +1764,7 @@ curl --include \ | track_count | integer | Number of tracks | | length_ms | integer | Total length of tracks in milliseconds | | uri | string | Resource identifier | +| artwork_url | string | *(optional)* [Artwork url](#artwork-urls) | ### `track` object @@ -1792,6 +1795,7 @@ curl --include \ | data_kind | string | Data type of this track: `file`, `stream`, `spotify`, `pipe` | | path | string | Path | | uri | string | Resource identifier | +| artwork_url | string | *(optional)* [Artwork url](#artwork-urls) | ### `paging` object @@ -1810,3 +1814,13 @@ curl --include \ | --------------- | -------- | ----------------------------------------- | | name | string | Name of genre | + + +### Artwork urls + +Artwork urls in `queue item`, `artist`, `album` and `track` objects can be either relative urls or absolute urls to the artwork image. +Absolute artwork urls are pointing to external artwork images (e. g. for radio streams that provide artwork metadata), while relative artwork urls are served from forked-daapd. + +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 diff --git a/src/artwork.h b/src/artwork.h index 0e64c85e..57b23a7a 100644 --- a/src/artwork.h +++ b/src/artwork.h @@ -5,6 +5,9 @@ #define ART_FMT_PNG 1 #define ART_FMT_JPEG 2 +#define ART_DEFAULT_HEIGHT 600 +#define ART_DEFAULT_WIDTH 600 + #include /* diff --git a/src/db.c b/src/db.c index b84bb7cd..556be4e9 100644 --- a/src/db.c +++ b/src/db.c @@ -4665,13 +4665,13 @@ queue_add_item(struct db_queue_item *item, int pos, int shuffle_pos, int queue_v "pos, shuffle_pos, path, virtual_path, title, " \ "artist, album_artist, album, genre, songalbumid, " \ "time_modified, artist_sort, album_sort, album_artist_sort, year, " \ - "track, disc, queue_version)" \ + "track, disc, artwork_url, queue_version)" \ "VALUES" \ "(NULL, %d, %d, %d, %d, " \ "%d, %d, %Q, %Q, %Q, " \ "%Q, %Q, %Q, %Q, %" PRIi64 ", " \ "%d, %Q, %Q, %Q, %d, " \ - "%d, %d, %d);" + "%d, %d, %Q, %d);" char *query; int ret; @@ -4681,7 +4681,7 @@ queue_add_item(struct db_queue_item *item, int pos, int shuffle_pos, int queue_v pos, shuffle_pos, item->path, item->virtual_path, item->title, item->artist, item->album_artist, item->album, item->genre, item->songalbumid, item->time_modified, item->artist_sort, item->album_sort, item->album_artist_sort, item->year, - item->track, item->disc, queue_version); + item->track, item->disc, item->artwork_url, queue_version); ret = db_query_run(query, 1, 0); return ret; diff --git a/src/http.c b/src/http.c index 25658f76..06315041 100644 --- a/src/http.c +++ b/src/http.c @@ -639,7 +639,7 @@ metadata_packet_get(struct http_icy_metadata *metadata, AVFormatContext *fmtctx) else metadata->title = strdup(metadata->title); } - else if ((strncmp(icy_token, "StreamUrl", strlen("StreamUrl")) == 0) && !metadata->artwork_url) + else if ((strncmp(icy_token, "StreamUrl", strlen("StreamUrl")) == 0) && !metadata->artwork_url && strlen(ptr) > 0) { metadata->artwork_url = strdup(ptr); } diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c index ca3eb84b..4a74d439 100644 --- a/src/httpd_jsonapi.c +++ b/src/httpd_jsonapi.c @@ -1525,7 +1525,8 @@ queue_item_to_json(struct db_queue_item *queue_item, char shuffle) else json_object_object_add(item, "position", json_object_new_int(queue_item->pos)); - json_object_object_add(item, "track_id", json_object_new_int(queue_item->file_id)); + if (queue_item->file_id > 0 && queue_item->file_id != DB_MEDIA_FILE_NON_PERSISTENT_ID) + json_object_object_add(item, "track_id", json_object_new_int(queue_item->file_id)); safe_json_add_string(item, "title", queue_item->title); safe_json_add_string(item, "artist", queue_item->artist); @@ -1546,21 +1547,27 @@ queue_item_to_json(struct db_queue_item *queue_item, char shuffle) safe_json_add_string(item, "path", queue_item->path); - if (queue_item->file_id > 0) + if (queue_item->file_id > 0 && queue_item->file_id != DB_MEDIA_FILE_NON_PERSISTENT_ID) { ret = snprintf(uri, sizeof(uri), "%s:%s:%d", "library", "track", queue_item->file_id); if (ret < sizeof(uri)) json_object_object_add(item, "uri", json_object_new_string(uri)); - - ret = snprintf(artwork_url, sizeof(artwork_url), "/artwork/item/%d", queue_item->file_id); - if (ret < sizeof(artwork_url)) - json_object_object_add(item, "artwork_url", json_object_new_string(artwork_url)); } else { safe_json_add_string(item, "uri", queue_item->path); + } + + if (queue_item->artwork_url) + { safe_json_add_string(item, "artwork_url", queue_item->artwork_url); } + else if (queue_item->file_id > 0 && queue_item->file_id != DB_MEDIA_FILE_NON_PERSISTENT_ID) + { + ret = snprintf(artwork_url, sizeof(artwork_url), "/artwork/item/%d", queue_item->file_id); + if (ret < sizeof(artwork_url)) + json_object_object_add(item, "artwork_url", json_object_new_string(artwork_url)); + } return item; } diff --git a/src/mpd.c b/src/mpd.c index 7ad9bdcb..dca63bb2 100644 --- a/src/mpd.c +++ b/src/mpd.c @@ -4702,7 +4702,7 @@ artwork_cb(struct evhttp_request *req, void *arg) return; } - format = artwork_get_item(evbuffer, itemid, 600, 600); + format = artwork_get_item(evbuffer, itemid, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT); if (format < 0) { httpd_send_error(req, HTTP_NOTFOUND, "Document was not found"); diff --git a/src/outputs/raop.c b/src/outputs/raop.c index d08982fd..0eca4a5c 100644 --- a/src/outputs/raop.c +++ b/src/outputs/raop.c @@ -894,7 +894,7 @@ raop_metadata_prepare(int id) goto skip_artwork; } - ret = artwork_get_item(rmd->artwork, queue_item->file_id, 600, 600); + ret = artwork_get_item(rmd->artwork, queue_item->file_id, ART_DEFAULT_WIDTH, ART_DEFAULT_HEIGHT); if (ret < 0) { DPRINTF(E_INFO, L_RAOP, "Failed to retrieve artwork for file id %d; no artwork will be sent\n", id); diff --git a/src/spotify_webapi.c b/src/spotify_webapi.c index 642de2c1..9e22a033 100644 --- a/src/spotify_webapi.c +++ b/src/spotify_webapi.c @@ -26,6 +26,7 @@ #include #include +#include "artwork.h" #include "cache.h" #include "conffile.h" #include "db.h" @@ -53,6 +54,7 @@ struct spotify_album const char *release_date_precision; int release_year; const char *uri; + const char *artwork_url; }; struct spotify_track @@ -71,6 +73,7 @@ struct spotify_track const char *name; int track_number; const char *uri; + const char *artwork_url; bool is_playable; const char *restrictions; @@ -577,12 +580,50 @@ request_pagingobject_endpoint(const char *href, paging_item_cb item_cb, paging_r return 0; } +static const char * +get_album_image(json_object *jsonalbum) +{ + json_object *jsonimages; + json_object *jsonimage; + int image_count; + int index; + const char *artwork_url; + int width; + int temp; + + artwork_url = NULL; + temp = 0; + width = 0; + + if (json_object_object_get_ex(jsonalbum, "images", &jsonimages)) + { + // Find image closest to ART_DEFAULT_WIDTH + image_count = json_object_array_length(jsonimages); + for (index = 0; index < image_count; index++) + { + jsonimage = json_object_array_get_idx(jsonimages, index); + if (jsonimage) + { + temp = jparse_int_from_obj(jsonimage, "width"); + + if (temp > width && temp < ART_DEFAULT_WIDTH) + { + artwork_url = jparse_str_from_obj(jsonimage, "url"); + width = temp; + } + } + } + } + + return artwork_url; +} + static void parse_metadata_track(json_object *jsontrack, struct spotify_track *track) { - json_object* jsonalbum; - json_object* jsonartists; - json_object* needle; + json_object *jsonalbum; + json_object *jsonartists; + json_object *needle; memset(track, 0, sizeof(struct spotify_track)); @@ -591,6 +632,8 @@ parse_metadata_track(json_object *jsontrack, struct spotify_track *track) track->album = jparse_str_from_obj(jsonalbum, "name"); if (json_object_object_get_ex(jsonalbum, "artists", &jsonartists)) track->album_artist = jparse_str_from_array(jsonartists, 0, "name"); + + track->artwork_url = get_album_image(jsonalbum); } if (json_object_object_get_ex(jsontrack, "artists", &jsonartists)) @@ -658,6 +701,8 @@ parse_metadata_album(json_object *jsonalbum, struct spotify_album *album) album->release_date_precision = jparse_str_from_obj(jsonalbum, "release_date_precision"); album->release_year = get_year_from_date(album->release_date); + album->artwork_url = get_album_image(jsonalbum); + // TODO Genre is an array of strings ('genres'), but it is always empty (https://github.com/spotify/web-api/issues/157) //album->genre = jparse_str_from_obj(jsonalbum, "genre"); } @@ -976,11 +1021,13 @@ map_track_to_queueitem(struct db_queue_item *item, const struct spotify_track *t { item->album_artist = safe_strdup(album->artist); item->album = safe_strdup(album->name); + item->artwork_url = safe_strdup(album->artwork_url); } else { item->album_artist = safe_strdup(track->album_artist); item->album = safe_strdup(track->album); + item->artwork_url = safe_strdup(track->artwork_url); } item->disc = track->disc_number;