Merge pull request #687 from chme/spotify_artwork_2
[spotify/artwork] Add spotify webapi as an additional artwork source
This commit is contained in:
commit
b1622b5b91
176
src/artwork.c
176
src/artwork.c
|
@ -42,6 +42,7 @@
|
||||||
#include "artwork.h"
|
#include "artwork.h"
|
||||||
|
|
||||||
#ifdef HAVE_SPOTIFY_H
|
#ifdef HAVE_SPOTIFY_H
|
||||||
|
# include "spotify_webapi.h"
|
||||||
# include "spotify.h"
|
# include "spotify.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -138,6 +139,7 @@ static int source_item_embedded_get(struct artwork_ctx *ctx);
|
||||||
static int source_item_own_get(struct artwork_ctx *ctx);
|
static int source_item_own_get(struct artwork_ctx *ctx);
|
||||||
static int source_item_stream_get(struct artwork_ctx *ctx);
|
static int source_item_stream_get(struct artwork_ctx *ctx);
|
||||||
static int source_item_spotify_get(struct artwork_ctx *ctx);
|
static int source_item_spotify_get(struct artwork_ctx *ctx);
|
||||||
|
static int source_item_spotifywebapi_get(struct artwork_ctx *ctx);
|
||||||
static int source_item_ownpl_get(struct artwork_ctx *ctx);
|
static int source_item_ownpl_get(struct artwork_ctx *ctx);
|
||||||
|
|
||||||
/* List of sources that can provide artwork for a group (i.e. usually an album
|
/* List of sources that can provide artwork for a group (i.e. usually an album
|
||||||
|
@ -200,6 +202,12 @@ static struct artwork_source artwork_item_source[] =
|
||||||
.data_kinds = (1 << DATA_KIND_SPOTIFY),
|
.data_kinds = (1 << DATA_KIND_SPOTIFY),
|
||||||
.cache = ON_SUCCESS,
|
.cache = ON_SUCCESS,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "Spotify web api",
|
||||||
|
.handler = source_item_spotifywebapi_get,
|
||||||
|
.data_kinds = (1 << DATA_KIND_SPOTIFY),
|
||||||
|
.cache = ON_SUCCESS | ON_FAILURE,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = "playlist own",
|
.name = "playlist own",
|
||||||
.handler = source_item_ownpl_get,
|
.handler = source_item_ownpl_get,
|
||||||
|
@ -218,6 +226,58 @@ static struct artwork_source artwork_item_source[] =
|
||||||
|
|
||||||
/* -------------------------------- HELPERS -------------------------------- */
|
/* -------------------------------- HELPERS -------------------------------- */
|
||||||
|
|
||||||
|
/* Reads an artwork file from the given url straight into an evbuf
|
||||||
|
*
|
||||||
|
* @out evbuf Image data
|
||||||
|
* @in url URL for the image
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
artwork_url_read(struct evbuffer *evbuf, const char *url)
|
||||||
|
{
|
||||||
|
struct http_client_ctx client;
|
||||||
|
struct keyval *kv;
|
||||||
|
const char *content_type;
|
||||||
|
int len;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
DPRINTF(E_SPAM, L_ART, "Trying internet artwork in %s\n", url);
|
||||||
|
|
||||||
|
ret = ART_E_NONE;
|
||||||
|
|
||||||
|
len = strlen(url);
|
||||||
|
if ((len < 14) || (len > PATH_MAX)) // Can't be shorter than http://a/1.jpg
|
||||||
|
goto out_url;
|
||||||
|
|
||||||
|
kv = keyval_alloc();
|
||||||
|
if (!kv)
|
||||||
|
goto out_url;
|
||||||
|
|
||||||
|
memset(&client, 0, sizeof(struct http_client_ctx));
|
||||||
|
client.url = url;
|
||||||
|
client.input_headers = kv;
|
||||||
|
client.input_body = evbuf;
|
||||||
|
|
||||||
|
if (http_client_request(&client) < 0)
|
||||||
|
goto out_kv;
|
||||||
|
|
||||||
|
if (client.response_code != HTTP_OK)
|
||||||
|
goto out_kv;
|
||||||
|
|
||||||
|
content_type = keyval_get(kv, "Content-Type");
|
||||||
|
if (content_type && (strcmp(content_type, "image/jpeg") == 0))
|
||||||
|
ret = ART_FMT_JPEG;
|
||||||
|
else if (content_type && (strcmp(content_type, "image/png") == 0))
|
||||||
|
ret = ART_FMT_PNG;
|
||||||
|
|
||||||
|
out_kv:
|
||||||
|
keyval_clear(kv);
|
||||||
|
free(kv);
|
||||||
|
|
||||||
|
out_url:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/* Reads an artwork file from the filesystem straight into an evbuf
|
/* Reads an artwork file from the filesystem straight into an evbuf
|
||||||
* TODO Use evbuffer_add_file or evbuffer_read?
|
* TODO Use evbuffer_add_file or evbuffer_read?
|
||||||
*
|
*
|
||||||
|
@ -322,9 +382,10 @@ rescale_calculate(int *target_w, int *target_h, int width, int height, int max_w
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get an artwork file from the filesystem. Will rescale if needed.
|
/*
|
||||||
|
* Either gets the artwork file given in "path" from the file system (rescaled if needed) or rescales the artwork given in "inbuf".
|
||||||
*
|
*
|
||||||
* @out evbuf Image data
|
* @out evbuf Image data (rescaled if needed)
|
||||||
* @in path Path to the artwork file (alternative to inbuf)
|
* @in path Path to the artwork file (alternative to inbuf)
|
||||||
* @in inbuf Buffer with the artwork (alternative to path)
|
* @in inbuf Buffer with the artwork (alternative to path)
|
||||||
* @in max_w Requested width
|
* @in max_w Requested width
|
||||||
|
@ -727,10 +788,7 @@ source_item_own_get(struct artwork_ctx *ctx)
|
||||||
static int
|
static int
|
||||||
source_item_stream_get(struct artwork_ctx *ctx)
|
source_item_stream_get(struct artwork_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct http_client_ctx client;
|
|
||||||
struct db_queue_item *queue_item;
|
struct db_queue_item *queue_item;
|
||||||
struct keyval *kv;
|
|
||||||
const char *content_type;
|
|
||||||
char *url;
|
char *url;
|
||||||
char *ext;
|
char *ext;
|
||||||
int len;
|
int len;
|
||||||
|
@ -764,34 +822,14 @@ source_item_stream_get(struct artwork_ctx *ctx)
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
goto out_url;
|
goto out_url;
|
||||||
|
|
||||||
kv = keyval_alloc();
|
ret = artwork_url_read(ctx->evbuf, url);
|
||||||
if (!kv)
|
|
||||||
goto out_url;
|
|
||||||
|
|
||||||
memset(&client, 0, sizeof(struct http_client_ctx));
|
|
||||||
client.url = url;
|
|
||||||
client.input_headers = kv;
|
|
||||||
client.input_body = ctx->evbuf;
|
|
||||||
|
|
||||||
if (http_client_request(&client) < 0)
|
|
||||||
goto out_kv;
|
|
||||||
|
|
||||||
content_type = keyval_get(kv, "Content-Type");
|
|
||||||
if (content_type && (strcmp(content_type, "image/jpeg") == 0))
|
|
||||||
ret = ART_FMT_JPEG;
|
|
||||||
else if (content_type && (strcmp(content_type, "image/png") == 0))
|
|
||||||
ret = ART_FMT_PNG;
|
|
||||||
|
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_SPAM, L_ART, "Found internet stream artwork in %s (%s)\n", url, content_type);
|
DPRINTF(E_SPAM, L_ART, "Found internet stream artwork in %s (%d)\n", url, ret);
|
||||||
cache_artwork_stash(ctx->evbuf, url, ret);
|
cache_artwork_stash(ctx->evbuf, url, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
out_kv:
|
|
||||||
keyval_clear(kv);
|
|
||||||
free(kv);
|
|
||||||
|
|
||||||
out_url:
|
out_url:
|
||||||
free(url);
|
free(url);
|
||||||
|
|
||||||
|
@ -867,12 +905,96 @@ source_item_spotify_get(struct artwork_ctx *ctx)
|
||||||
|
|
||||||
return ART_E_ERROR;
|
return ART_E_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
source_item_spotifywebapi_get(struct artwork_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct evbuffer *raw;
|
||||||
|
struct evbuffer *evbuf;
|
||||||
|
char *artwork_url;
|
||||||
|
int content_type;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
artwork_url = NULL;
|
||||||
|
raw = evbuffer_new();
|
||||||
|
evbuf = evbuffer_new();
|
||||||
|
if (!raw || !evbuf)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_ART, "Out of memory for Spotify evbuf\n");
|
||||||
|
return ART_E_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
artwork_url = spotifywebapi_artwork_url_get(ctx->dbmfi->path, ctx->max_w, ctx->max_h);
|
||||||
|
if (!artwork_url)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_ART, "No artwork from Spotify for %s\n", ctx->dbmfi->path);
|
||||||
|
return ART_E_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = artwork_url_read(raw, artwork_url);
|
||||||
|
if (ret <= 0)
|
||||||
|
goto out_free_evbuf;
|
||||||
|
|
||||||
|
content_type = ret;
|
||||||
|
|
||||||
|
// Make a refbuf of raw for ffmpeg image size probing and possibly rescaling.
|
||||||
|
// We keep raw around in case rescaling is not necessary.
|
||||||
|
#ifdef HAVE_LIBEVENT2_OLD
|
||||||
|
uint8_t *buf = evbuffer_pullup(raw, -1);
|
||||||
|
if (!buf)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_ART, "Could not pullup raw artwork\n");
|
||||||
|
goto out_free_evbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = evbuffer_add_reference(evbuf, buf, evbuffer_get_length(raw), NULL, NULL);
|
||||||
|
#else
|
||||||
|
ret = evbuffer_add_buffer_reference(evbuf, raw);
|
||||||
|
#endif
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_ART, "Could not copy/ref raw image for ffmpeg\n");
|
||||||
|
goto out_free_evbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For non-file input, artwork_get() will also fail if no rescaling is required
|
||||||
|
ret = artwork_get(ctx->evbuf, NULL, evbuf, ctx->max_w, ctx->max_h, false);
|
||||||
|
if (ret == ART_E_ERROR)
|
||||||
|
{
|
||||||
|
DPRINTF(E_DBG, L_ART, "Not rescaling Spotify image\n");
|
||||||
|
ret = evbuffer_add_buffer(ctx->evbuf, raw);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_ART, "Could not add or rescale image to output evbuf\n");
|
||||||
|
goto out_free_evbuf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evbuffer_free(evbuf);
|
||||||
|
evbuffer_free(raw);
|
||||||
|
free(artwork_url);
|
||||||
|
|
||||||
|
return content_type;
|
||||||
|
|
||||||
|
out_free_evbuf:
|
||||||
|
evbuffer_free(evbuf);
|
||||||
|
evbuffer_free(raw);
|
||||||
|
free(artwork_url);
|
||||||
|
|
||||||
|
return ART_E_ERROR;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
static int
|
static int
|
||||||
source_item_spotify_get(struct artwork_ctx *ctx)
|
source_item_spotify_get(struct artwork_ctx *ctx)
|
||||||
{
|
{
|
||||||
return ART_E_ERROR;
|
return ART_E_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
source_item_spotifywebapi_get(struct artwork_ctx *ctx)
|
||||||
|
{
|
||||||
|
return ART_E_ERROR;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* First looks of the mfi->path is in any playlist, and if so looks in the dir
|
/* First looks of the mfi->path is in any playlist, and if so looks in the dir
|
||||||
|
|
31
src/http.c
31
src/http.c
|
@ -100,7 +100,6 @@ request_cb(struct evhttp_request *req, void *arg)
|
||||||
{
|
{
|
||||||
struct http_client_ctx *ctx;
|
struct http_client_ctx *ctx;
|
||||||
const char *response_code_line;
|
const char *response_code_line;
|
||||||
int response_code;
|
|
||||||
|
|
||||||
ctx = (struct http_client_ctx *)arg;
|
ctx = (struct http_client_ctx *)arg;
|
||||||
|
|
||||||
|
@ -119,21 +118,21 @@ request_cb(struct evhttp_request *req, void *arg)
|
||||||
goto connection_error;
|
goto connection_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
response_code = evhttp_request_get_response_code(req);
|
ctx->response_code = evhttp_request_get_response_code(req);
|
||||||
#ifndef HAVE_LIBEVENT2_OLD
|
#ifndef HAVE_LIBEVENT2_OLD
|
||||||
response_code_line = evhttp_request_get_response_code_line(req);
|
response_code_line = evhttp_request_get_response_code_line(req);
|
||||||
#else
|
#else
|
||||||
response_code_line = "no error text";
|
response_code_line = "no error text";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (response_code == 0)
|
if (ctx->response_code == 0)
|
||||||
{
|
{
|
||||||
DPRINTF(E_WARN, L_HTTP, "Connection to %s failed: Connection refused\n", ctx->url);
|
DPRINTF(E_WARN, L_HTTP, "Connection to %s failed: Connection refused\n", ctx->url);
|
||||||
goto connection_error;
|
goto connection_error;
|
||||||
}
|
}
|
||||||
else if (response_code != 200)
|
else if (ctx->response_code != 200)
|
||||||
{
|
{
|
||||||
DPRINTF(E_WARN, L_HTTP, "Connection to %s failed: %s (error %d)\n", ctx->url, response_code_line, response_code);
|
DPRINTF(E_WARN, L_HTTP, "Connection to %s failed: %s (error %d)\n", ctx->url, response_code_line, ctx->response_code);
|
||||||
goto connection_error;
|
goto connection_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,6 +308,23 @@ http_client_request_impl(struct http_client_ctx *ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LIBCURL
|
#ifdef HAVE_LIBCURL
|
||||||
|
|
||||||
|
static void
|
||||||
|
curl_headers_save(struct keyval *kv, CURL *curl)
|
||||||
|
{
|
||||||
|
char *content_type;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!kv || !curl)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type);
|
||||||
|
if (ret == CURLE_OK && content_type)
|
||||||
|
{
|
||||||
|
keyval_add(kv, "Content-Type", content_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
curl_request_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
|
curl_request_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||||
{
|
{
|
||||||
|
@ -341,6 +357,7 @@ https_client_request_impl(struct http_client_ctx *ctx)
|
||||||
struct onekeyval *okv;
|
struct onekeyval *okv;
|
||||||
const char *user_agent;
|
const char *user_agent;
|
||||||
char header[1024];
|
char header[1024];
|
||||||
|
long response_code;
|
||||||
|
|
||||||
curl = curl_easy_init();
|
curl = curl_easy_init();
|
||||||
if (!curl)
|
if (!curl)
|
||||||
|
@ -384,6 +401,10 @@ https_client_request_impl(struct http_client_ctx *ctx)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||||
|
ctx->response_code = (int) response_code;
|
||||||
|
curl_headers_save(ctx->input_headers, curl);
|
||||||
|
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -29,6 +29,9 @@ struct http_client_ctx
|
||||||
*/
|
*/
|
||||||
int headers_only;
|
int headers_only;
|
||||||
|
|
||||||
|
/* HTTP Response code */
|
||||||
|
int response_code;
|
||||||
|
|
||||||
/* Private */
|
/* Private */
|
||||||
int ret;
|
int ret;
|
||||||
void *evbase;
|
void *evbase;
|
||||||
|
|
|
@ -93,7 +93,6 @@ struct spotify_playlist
|
||||||
int tracks_count;
|
int tracks_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Credentials for the web api
|
// Credentials for the web api
|
||||||
static char *spotify_access_token;
|
static char *spotify_access_token;
|
||||||
static char *spotify_refresh_token;
|
static char *spotify_refresh_token;
|
||||||
|
@ -581,36 +580,42 @@ request_pagingobject_endpoint(const char *href, paging_item_cb item_cb, paging_r
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
get_album_image(json_object *jsonalbum)
|
get_album_image(json_object *jsonalbum, int max_w)
|
||||||
{
|
{
|
||||||
json_object *jsonimages;
|
json_object *jsonimages;
|
||||||
json_object *jsonimage;
|
json_object *jsonimage;
|
||||||
int image_count;
|
int image_count;
|
||||||
int index;
|
int index;
|
||||||
const char *artwork_url;
|
const char *artwork_url;
|
||||||
int width;
|
|
||||||
int temp;
|
|
||||||
|
|
||||||
artwork_url = NULL;
|
artwork_url = NULL;
|
||||||
temp = 0;
|
|
||||||
width = 0;
|
|
||||||
|
|
||||||
if (json_object_object_get_ex(jsonalbum, "images", &jsonimages))
|
if (max_w <= 0)
|
||||||
{
|
{
|
||||||
// Find image closest to ART_DEFAULT_WIDTH
|
return NULL;
|
||||||
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)
|
if (!json_object_object_get_ex(jsonalbum, "images", &jsonimages))
|
||||||
{
|
{
|
||||||
artwork_url = jparse_str_from_obj(jsonimage, "url");
|
DPRINTF(E_DBG, L_SPOTIFY, "No images in for spotify album object found\n");
|
||||||
width = temp;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find first image that has a smaller width than the given max_w
|
||||||
|
// (this should avoid the need for resizing and improve performance at the cost of some quality loss)
|
||||||
|
// Note that Spotify returns the images ordered descending by width (widest image first)
|
||||||
|
image_count = json_object_array_length(jsonimages);
|
||||||
|
for (index = 0; index < image_count; index++)
|
||||||
|
{
|
||||||
|
jsonimage = json_object_array_get_idx(jsonimages, index);
|
||||||
|
if (jsonimage)
|
||||||
|
{
|
||||||
|
artwork_url = jparse_str_from_obj(jsonimage, "url");
|
||||||
|
|
||||||
|
if (jparse_int_from_obj(jsonimage, "width") <= max_w)
|
||||||
|
{
|
||||||
|
// We have the first image that has a smaller width than the given max_w
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -619,7 +624,7 @@ get_album_image(json_object *jsonalbum)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
parse_metadata_track(json_object *jsontrack, struct spotify_track *track)
|
parse_metadata_track(json_object *jsontrack, struct spotify_track *track, int max_w)
|
||||||
{
|
{
|
||||||
json_object *jsonalbum;
|
json_object *jsonalbum;
|
||||||
json_object *jsonartists;
|
json_object *jsonartists;
|
||||||
|
@ -633,7 +638,8 @@ parse_metadata_track(json_object *jsontrack, struct spotify_track *track)
|
||||||
if (json_object_object_get_ex(jsonalbum, "artists", &jsonartists))
|
if (json_object_object_get_ex(jsonalbum, "artists", &jsonartists))
|
||||||
track->album_artist = jparse_str_from_array(jsonartists, 0, "name");
|
track->album_artist = jparse_str_from_array(jsonartists, 0, "name");
|
||||||
|
|
||||||
track->artwork_url = get_album_image(jsonalbum);
|
if (max_w > 0)
|
||||||
|
track->artwork_url = get_album_image(jsonalbum, max_w);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json_object_object_get_ex(jsontrack, "artists", &jsonartists))
|
if (json_object_object_get_ex(jsontrack, "artists", &jsonartists))
|
||||||
|
@ -679,7 +685,7 @@ get_year_from_date(const char *date)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
parse_metadata_album(json_object *jsonalbum, struct spotify_album *album)
|
parse_metadata_album(json_object *jsonalbum, struct spotify_album *album, int max_w)
|
||||||
{
|
{
|
||||||
json_object* jsonartists;
|
json_object* jsonartists;
|
||||||
|
|
||||||
|
@ -701,7 +707,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_date_precision = jparse_str_from_obj(jsonalbum, "release_date_precision");
|
||||||
album->release_year = get_year_from_date(album->release_date);
|
album->release_year = get_year_from_date(album->release_date);
|
||||||
|
|
||||||
album->artwork_url = get_album_image(jsonalbum);
|
if (max_w > 0)
|
||||||
|
album->artwork_url = get_album_image(jsonalbum, max_w);
|
||||||
|
|
||||||
// TODO Genre is an array of strings ('genres'), but it is always empty (https://github.com/spotify/web-api/issues/157)
|
// 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");
|
//album->genre = jparse_str_from_obj(jsonalbum, "genre");
|
||||||
|
@ -1056,7 +1063,7 @@ queue_add_track(const char *uri, int position, char reshuffle, uint32_t item_id,
|
||||||
if (!response)
|
if (!response)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
parse_metadata_track(response, &track);
|
parse_metadata_track(response, &track, ART_DEFAULT_WIDTH);
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_SPOTIFY, "Got track: '%s' (%s) \n", track.name, track.uri);
|
DPRINTF(E_DBG, L_SPOTIFY, "Got track: '%s' (%s) \n", track.name, track.uri);
|
||||||
|
|
||||||
|
@ -1097,7 +1104,7 @@ queue_add_album_tracks(json_object *item, int index, int total, void *arg)
|
||||||
|
|
||||||
param = arg;
|
param = arg;
|
||||||
|
|
||||||
parse_metadata_track(item, &track);
|
parse_metadata_track(item, &track, ART_DEFAULT_WIDTH);
|
||||||
|
|
||||||
if (!track.uri || !track.is_playable)
|
if (!track.uri || !track.is_playable)
|
||||||
{
|
{
|
||||||
|
@ -1125,7 +1132,7 @@ queue_add_album(const char *uri, int position, char reshuffle, uint32_t item_id,
|
||||||
|
|
||||||
album_endpoint_uri = get_album_endpoint_uri(uri);
|
album_endpoint_uri = get_album_endpoint_uri(uri);
|
||||||
json_album = request_endpoint_with_token_refresh(album_endpoint_uri);
|
json_album = request_endpoint_with_token_refresh(album_endpoint_uri);
|
||||||
parse_metadata_album(json_album, ¶m.album);
|
parse_metadata_album(json_album, ¶m.album, ART_DEFAULT_WIDTH);
|
||||||
|
|
||||||
ret = db_queue_add_start(¶m.queue_add_info, position);
|
ret = db_queue_add_start(¶m.queue_add_info, position);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
@ -1164,7 +1171,7 @@ queue_add_playlist_tracks(json_object *item, int index, int total, void *arg)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_metadata_track(jsontrack, &track);
|
parse_metadata_track(jsontrack, &track, ART_DEFAULT_WIDTH);
|
||||||
track.added_at = jparse_str_from_obj(item, "added_at");
|
track.added_at = jparse_str_from_obj(item, "added_at");
|
||||||
track.mtime = jparse_time_from_obj(item, "added_at");
|
track.mtime = jparse_time_from_obj(item, "added_at");
|
||||||
|
|
||||||
|
@ -1436,7 +1443,7 @@ saved_album_add(json_object *item, int index, int total, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map album information
|
// Map album information
|
||||||
parse_metadata_album(jsonalbum, &album);
|
parse_metadata_album(jsonalbum, &album, 0);
|
||||||
album.added_at = jparse_str_from_obj(item, "added_at");
|
album.added_at = jparse_str_from_obj(item, "added_at");
|
||||||
album.mtime = jparse_time_from_obj(item, "added_at");
|
album.mtime = jparse_time_from_obj(item, "added_at");
|
||||||
|
|
||||||
|
@ -1453,7 +1460,7 @@ saved_album_add(json_object *item, int index, int total, void *arg)
|
||||||
if (!jsontrack)
|
if (!jsontrack)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
parse_metadata_track(jsontrack, &track);
|
parse_metadata_track(jsontrack, &track, 0);
|
||||||
track.mtime = album.mtime;
|
track.mtime = album.mtime;
|
||||||
|
|
||||||
ret = track_add(&track, &album, NULL, dir_id);
|
ret = track_add(&track, &album, NULL, dir_id);
|
||||||
|
@ -1506,7 +1513,7 @@ saved_playlist_tracks_add(json_object *item, int index, int total, void *arg)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_metadata_track(jsontrack, &track);
|
parse_metadata_track(jsontrack, &track, 0);
|
||||||
track.added_at = jparse_str_from_obj(item, "added_at");
|
track.added_at = jparse_str_from_obj(item, "added_at");
|
||||||
track.mtime = jparse_time_from_obj(item, "added_at");
|
track.mtime = jparse_time_from_obj(item, "added_at");
|
||||||
|
|
||||||
|
@ -1827,6 +1834,29 @@ spotifywebapi_pl_remove(const char *uri)
|
||||||
library_exec_async(webapi_pl_remove, strdup(uri));
|
library_exec_async(webapi_pl_remove, strdup(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
spotifywebapi_artwork_url_get(const char *uri, int max_w, int max_h)
|
||||||
|
{
|
||||||
|
json_object *response;
|
||||||
|
struct spotify_track track;
|
||||||
|
char *artwork_url;
|
||||||
|
|
||||||
|
response = request_track(uri);
|
||||||
|
if (!response)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_metadata_track(response, &track, max_w);
|
||||||
|
|
||||||
|
DPRINTF(E_DBG, L_SPOTIFY, "Got track artwork url: '%s' (%s) \n", track.artwork_url, track.uri);
|
||||||
|
|
||||||
|
artwork_url = safe_strdup(track.artwork_url);
|
||||||
|
jparse_free(response);
|
||||||
|
|
||||||
|
return artwork_url;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
spotifywebapi_status_info_get(struct spotifywebapi_status_info *info)
|
spotifywebapi_status_info_get(struct spotifywebapi_status_info *info)
|
||||||
{
|
{
|
||||||
|
|
|
@ -53,6 +53,8 @@ void
|
||||||
spotifywebapi_pl_save(const char *uri);
|
spotifywebapi_pl_save(const char *uri);
|
||||||
void
|
void
|
||||||
spotifywebapi_pl_remove(const char *uri);
|
spotifywebapi_pl_remove(const char *uri);
|
||||||
|
char *
|
||||||
|
spotifywebapi_artwork_url_get(const char *uri, int max_w, int max_h);
|
||||||
|
|
||||||
void
|
void
|
||||||
spotifywebapi_status_info_get(struct spotifywebapi_status_info *info);
|
spotifywebapi_status_info_get(struct spotifywebapi_status_info *info);
|
||||||
|
|
Loading…
Reference in New Issue