Revert "[spotify/artwork] Load artwork for spotify through the wep api" and "[spotify] Thread safety for the webapi access"

Wait with this until it becomes necessary or we can achieve same performance as libspotify

This reverts commit 997b4da4ad and 2da993cc7b
This commit is contained in:
ejurgensen 2017-12-27 18:51:11 +01:00
parent 2da993cc7b
commit 4802823f3c
6 changed files with 315 additions and 343 deletions

View File

@ -42,7 +42,7 @@
#include "artwork.h" #include "artwork.h"
#ifdef HAVE_SPOTIFY_H #ifdef HAVE_SPOTIFY_H
# include "spotify_webapi.h" # include "spotify.h"
#endif #endif
/* This artwork module will look for artwork by consulting a set of sources one /* This artwork module will look for artwork by consulting a set of sources one
@ -218,57 +218,6 @@ 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;
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;
else
ret = ART_FMT_JPEG;
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?
* *
@ -778,7 +727,10 @@ 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;
@ -812,14 +764,34 @@ source_item_stream_get(struct artwork_ctx *ctx)
if (ret > 0) if (ret > 0)
goto out_url; goto out_url;
ret = artwork_url_read(ctx->evbuf, 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 = 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 (%d)\n", url, ret); DPRINTF(E_SPAM, L_ART, "Found internet stream artwork in %s (%s)\n", url, content_type);
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);
@ -832,11 +804,8 @@ source_item_spotify_get(struct artwork_ctx *ctx)
{ {
struct evbuffer *raw; struct evbuffer *raw;
struct evbuffer *evbuf; struct evbuffer *evbuf;
char *artwork_url;
int content_type;
int ret; int ret;
artwork_url = NULL;
raw = evbuffer_new(); raw = evbuffer_new();
evbuf = evbuffer_new(); evbuf = evbuffer_new();
if (!raw || !evbuf) if (!raw || !evbuf)
@ -845,19 +814,15 @@ source_item_spotify_get(struct artwork_ctx *ctx)
return ART_E_ERROR; return ART_E_ERROR;
} }
artwork_url = spotifywebapi_artwork_get(ctx->dbmfi->path, ctx->max_w, ctx->max_h); ret = spotify_artwork_get(raw, ctx->dbmfi->path, ctx->max_w, ctx->max_h);
if (!artwork_url) if (ret < 0)
{ {
DPRINTF(E_WARN, L_ART, "No artwork from Spotify for %s\n", ctx->dbmfi->path); DPRINTF(E_WARN, L_ART, "No artwork from Spotify for %s\n", ctx->dbmfi->path);
evbuffer_free(raw);
evbuffer_free(evbuf);
return ART_E_NONE; 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. // Make a refbuf of raw for ffmpeg image size probing and possibly rescaling.
// We keep raw around in case rescaling is not necessary. // We keep raw around in case rescaling is not necessary.
#ifdef HAVE_LIBEVENT2_OLD #ifdef HAVE_LIBEVENT2_OLD
@ -893,14 +858,12 @@ source_item_spotify_get(struct artwork_ctx *ctx)
evbuffer_free(evbuf); evbuffer_free(evbuf);
evbuffer_free(raw); evbuffer_free(raw);
free(artwork_url);
return content_type; return ART_FMT_JPEG;
out_free_evbuf: out_free_evbuf:
evbuffer_free(evbuf); evbuffer_free(evbuf);
evbuffer_free(raw); evbuffer_free(raw);
free(artwork_url);
return ART_E_ERROR; return ART_E_ERROR;
} }

View File

@ -46,7 +46,7 @@
#include "artwork.h" #include "artwork.h"
#ifdef HAVE_SPOTIFY_H #ifdef HAVE_SPOTIFY_H
# include "spotify_webapi.h" # include "spotify.h"
#endif #endif
#include "ffmpeg-compat.h" #include "ffmpeg-compat.h"
@ -224,57 +224,6 @@ 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;
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;
else
ret = ART_FMT_JPEG;
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?
* *
@ -1154,7 +1103,10 @@ 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;
@ -1188,14 +1140,34 @@ source_item_stream_get(struct artwork_ctx *ctx)
if (ret > 0) if (ret > 0)
goto out_url; goto out_url;
ret = artwork_url_read(ctx->evbuf, 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 = 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 (%d)\n", url, ret); DPRINTF(E_SPAM, L_ART, "Found internet stream artwork in %s (%s)\n", url, content_type);
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);
@ -1211,13 +1183,10 @@ source_item_spotify_get(struct artwork_ctx *ctx)
AVInputFormat *ifmt; AVInputFormat *ifmt;
struct evbuffer *raw; struct evbuffer *raw;
struct evbuffer *evbuf; struct evbuffer *evbuf;
char *artwork_url;
int content_type;
int target_w; int target_w;
int target_h; int target_h;
int ret; int ret;
artwork_url = NULL;
raw = evbuffer_new(); raw = evbuffer_new();
evbuf = evbuffer_new(); evbuf = evbuffer_new();
if (!raw || !evbuf) if (!raw || !evbuf)
@ -1226,19 +1195,15 @@ source_item_spotify_get(struct artwork_ctx *ctx)
return ART_E_ERROR; return ART_E_ERROR;
} }
artwork_url = spotifywebapi_artwork_get(ctx->dbmfi->path, ctx->max_w, ctx->max_h); ret = spotify_artwork_get(raw, ctx->dbmfi->path, ctx->max_w, ctx->max_h);
if (!artwork_url) if (ret < 0)
{ {
DPRINTF(E_WARN, L_ART, "No artwork from Spotify for %s\n", ctx->dbmfi->path); DPRINTF(E_WARN, L_ART, "No artwork from Spotify for %s\n", ctx->dbmfi->path);
evbuffer_free(raw);
evbuffer_free(evbuf);
return ART_E_NONE; 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. // Make a refbuf of raw for ffmpeg image size probing and possibly rescaling.
// We keep raw around in case rescaling is not necessary. // We keep raw around in case rescaling is not necessary.
#ifdef HAVE_LIBEVENT2_OLD #ifdef HAVE_LIBEVENT2_OLD
@ -1313,7 +1278,7 @@ source_item_spotify_get(struct artwork_ctx *ctx)
evbuffer_free(evbuf); evbuffer_free(evbuf);
evbuffer_free(raw); evbuffer_free(raw);
return content_type; return ART_FMT_JPEG;
out_close_input: out_close_input:
avformat_close_input(&src_ctx); avformat_close_input(&src_ctx);

View File

@ -141,6 +141,9 @@ static bool scanning;
static pthread_mutex_t status_lck; static pthread_mutex_t status_lck;
static struct spotify_status_info spotify_status_info; static struct spotify_status_info spotify_status_info;
// Timeout timespec
static struct timespec spotify_artwork_timeout = { SPOTIFY_ARTWORK_TIMEOUT, 0 };
// Audio buffer // Audio buffer
static struct evbuffer *spotify_audio_buffer; static struct evbuffer *spotify_audio_buffer;
@ -888,6 +891,194 @@ playback_eot(void *arg, int *retval)
return COMMAND_END; return COMMAND_END;
} }
static void
artwork_loaded_cb(sp_image *image, void *userdata)
{
struct artwork_get_param *artwork;
artwork = userdata;
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&artwork->mutex));
artwork->is_loaded = 1;
CHECK_ERR(L_SPOTIFY, pthread_cond_signal(&artwork->cond));
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&artwork->mutex));
}
static enum command_state
artwork_get_bh(void *arg, int *retval)
{
struct artwork_get_param *artwork;
sp_imageformat imageformat;
sp_error err;
const void *data;
char *path;
size_t data_size;
int ret;
artwork = arg;
sp_image *image = artwork->image;
path = artwork->path;
fptr_sp_image_remove_load_callback(image, artwork_loaded_cb, artwork);
err = fptr_sp_image_error(image);
if (err != SP_ERROR_OK)
{
DPRINTF(E_WARN, L_SPOTIFY, "Getting artwork (%s) failed, Spotify error: %s\n", path, fptr_sp_error_message(err));
goto fail;
}
if (!fptr_sp_image_is_loaded(image))
{
DPRINTF(E_LOG, L_SPOTIFY, "Load callback returned, but no image? Possible bug: %s\n", path);
goto fail;
}
imageformat = fptr_sp_image_format(image);
if (imageformat != SP_IMAGE_FORMAT_JPEG)
{
DPRINTF(E_WARN, L_SPOTIFY, "Getting artwork failed, invalid image format from Spotify: %s\n", path);
goto fail;
}
data_size = 0;
data = fptr_sp_image_data(image, &data_size);
if (!data)
{
DPRINTF(E_LOG, L_SPOTIFY, "Getting artwork failed, no image data from Spotify: %s\n", path);
goto fail;
}
if ((data_size < 200) || (data_size > 20000000))
{
// Sometimes we get strange data size even though fptr_sp_image_data returns success
DPRINTF(E_LOG, L_SPOTIFY, "Skipping artwork, data size is weird (%zu)\n", data_size);
goto fail;
}
ret = evbuffer_expand(artwork->evbuf, data_size);
if (ret < 0)
{
DPRINTF(E_LOG, L_SPOTIFY, "Out of memory for artwork (data size requested was %zu)\n", data_size);
goto fail;
}
ret = evbuffer_add(artwork->evbuf, data, data_size);
if (ret < 0)
{
DPRINTF(E_LOG, L_SPOTIFY, "Could not add Spotify image to event buffer\n");
goto fail;
}
DPRINTF(E_DBG, L_SPOTIFY, "Spotify artwork loaded ok\n");
fptr_sp_image_release(image);
*retval = 0;
return COMMAND_END;
fail:
fptr_sp_image_release(image);
*retval = -1;
return COMMAND_END;
}
static enum command_state
artwork_get(void *arg, int *retval)
{
struct artwork_get_param *artwork;
char *path;
sp_link *link;
sp_track *track;
sp_album *album;
const byte *image_id;
sp_image *image;
sp_image_size image_size;
sp_error err;
artwork = arg;
path = artwork->path;
// Now begins: path -> link -> track -> album -> image_id -> image -> format -> data
link = fptr_sp_link_create_from_string(path);
if (!link)
{
DPRINTF(E_WARN, L_SPOTIFY, "Getting artwork failed, invalid Spotify link: %s\n", path);
goto level1_exit;
}
track = fptr_sp_link_as_track(link);
if (!track)
{
DPRINTF(E_WARN, L_SPOTIFY, "Getting artwork failed, invalid Spotify track: %s\n", path);
goto level2_exit;
}
album = fptr_sp_track_album(track);
if (!album)
{
DPRINTF(E_WARN, L_SPOTIFY, "Getting artwork failed, invalid Spotify album: %s\n", path);
goto level2_exit;
}
// Get an image at least the same size as requested
image_size = SP_IMAGE_SIZE_SMALL; // 64x64
if ((artwork->max_w > 64) || (artwork->max_h > 64))
image_size = SP_IMAGE_SIZE_NORMAL; // 300x300
if ((artwork->max_w > 300) || (artwork->max_h > 300))
image_size = SP_IMAGE_SIZE_LARGE; // 640x640
image_id = fptr_sp_album_cover(album, image_size);
if (!image_id)
{
DPRINTF(E_DBG, L_SPOTIFY, "Getting artwork failed, no Spotify image id: %s\n", path);
goto level2_exit;
}
image = fptr_sp_image_create(g_sess, image_id);
if (!image)
{
DPRINTF(E_DBG, L_SPOTIFY, "Getting artwork failed, no Spotify image: %s\n", path);
goto level2_exit;
}
fptr_sp_link_release(link);
artwork->image = image;
artwork->is_loaded = fptr_sp_image_is_loaded(image);
/* If the image is ready we can return it straight away, otherwise we will
* let the calling thread wait, since the Spotify thread should not wait
*/
if (artwork->is_loaded)
return artwork_get_bh(artwork, retval);
DPRINTF(E_SPAM, L_SPOTIFY, "Will wait for Spotify to call artwork_loaded_cb\n");
/* Async - we will return to spotify_artwork_get which will wait for callback */
err = fptr_sp_image_add_load_callback(image, artwork_loaded_cb, artwork);
if (err != SP_ERROR_OK)
{
DPRINTF(E_WARN, L_SPOTIFY, "Adding artwork cb failed, Spotify error: %s\n", fptr_sp_error_message(err));
*retval = -1;
return COMMAND_END;
}
*retval = 0;
return COMMAND_END;
level2_exit:
fptr_sp_link_release(link);
level1_exit:
*retval = -1;
return COMMAND_END;
}
/* --------------------------- SESSION CALLBACKS ------------------------- */ /* --------------------------- SESSION CALLBACKS ------------------------- */
/** /**
* This callback is called when an attempt to login has succeeded or failed. * This callback is called when an attempt to login has succeeded or failed.
@ -1258,6 +1449,41 @@ spotify_playback_seek(int ms)
return -1; return -1;
} }
/* Thread: httpd (artwork) and worker */
int
spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h)
{
struct artwork_get_param artwork;
struct timespec ts;
int ret;
artwork.evbuf = evbuf;
artwork.path = path;
artwork.max_w = max_w;
artwork.max_h = max_h;
CHECK_ERR(L_SPOTIFY, mutex_init(&artwork.mutex));
CHECK_ERR(L_SPOTIFY, pthread_cond_init(&artwork.cond, NULL));
ret = commands_exec_sync(cmdbase, artwork_get, NULL, &artwork);
// Artwork was not ready, wait for callback from libspotify
if (ret == 0)
{
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&artwork.mutex));
ts = timespec_reltoabs(spotify_artwork_timeout);
while ((!artwork.is_loaded) && (ret != ETIMEDOUT))
CHECK_ERR_EXCEPT(L_SPOTIFY, pthread_cond_timedwait(&artwork.cond, &artwork.mutex, &ts), ret, ETIMEDOUT);
if (ret == ETIMEDOUT)
DPRINTF(E_LOG, L_SPOTIFY, "Timeout waiting for artwork from Spotify\n");
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&artwork.mutex));
ret = commands_exec_sync(cmdbase, artwork_get_bh, NULL, &artwork);
}
return ret;
}
/* Thread: httpd */ /* Thread: httpd */
void void
spotify_oauth_interface(struct evbuffer *evbuf, const char *redirect_uri) spotify_oauth_interface(struct evbuffer *evbuf, const char *redirect_uri)
@ -1967,9 +2193,8 @@ spotify_init(void)
CHECK_ERR(L_SPOTIFY, mutex_init(&login_lck)); CHECK_ERR(L_SPOTIFY, mutex_init(&login_lck));
CHECK_ERR(L_SPOTIFY, pthread_cond_init(&login_cond, NULL)); CHECK_ERR(L_SPOTIFY, pthread_cond_init(&login_cond, NULL));
CHECK_ERR(L_SPOTIFY, mutex_init(&status_lck));
spotifywebapi_init(); CHECK_ERR(L_SPOTIFY, mutex_init(&status_lck));
/* Spawn thread */ /* Spawn thread */
ret = pthread_create(&tid_spotify, NULL, spotify, NULL); ret = pthread_create(&tid_spotify, NULL, spotify, NULL);
@ -1989,8 +2214,6 @@ spotify_init(void)
return 0; return 0;
thread_fail: thread_fail:
spotifywebapi_deinit();
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&status_lck)); CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&status_lck));
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond)); CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond));
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck)); CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck));
@ -2057,9 +2280,6 @@ spotify_deinit(void)
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond)); CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond));
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck)); CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&login_lck));
/* Deinit web api */
spotifywebapi_deinit();
/* Free audio buffer */ /* Free audio buffer */
evbuffer_free(spotify_audio_buffer); evbuffer_free(spotify_audio_buffer);

View File

@ -39,6 +39,9 @@ spotify_playback_stop_nonblock(void);
int int
spotify_playback_seek(int ms); spotify_playback_seek(int ms);
int
spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h);
void void
spotify_oauth_interface(struct evbuffer *evbuf, const char *redirect_uri); spotify_oauth_interface(struct evbuffer *evbuf, const char *redirect_uri);

View File

@ -43,15 +43,12 @@ static char *spotify_user;
static int32_t expires_in = 3600; static int32_t expires_in = 3600;
static time_t token_requested = 0; static time_t token_requested = 0;
static pthread_mutex_t token_lck;
// Endpoints and credentials for the web api // Endpoints and credentials for the web api
static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789"; static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789";
static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239"; static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239";
static const char *spotify_auth_uri = "https://accounts.spotify.com/authorize"; static const char *spotify_auth_uri = "https://accounts.spotify.com/authorize";
static const char *spotify_token_uri = "https://accounts.spotify.com/api/token"; static const char *spotify_token_uri = "https://accounts.spotify.com/api/token";
static const char *spotify_playlist_uri = "https://api.spotify.com/v1/users/%s/playlists/%s"; static const char *spotify_playlist_uri = "https://api.spotify.com/v1/users/%s/playlists/%s";
static const char *spotify_track_uri = "https://api.spotify.com/v1/tracks/%s";
static const char *spotify_me_uri = "https://api.spotify.com/v1/me"; static const char *spotify_me_uri = "https://api.spotify.com/v1/me";
@ -75,14 +72,14 @@ free_http_client_ctx(struct http_client_ctx *ctx)
} }
static int static int
request_uri(struct spotify_request *request, const char *uri, bool check_token) request_uri(struct spotify_request *request, const char *uri)
{ {
char bearer_token[1024]; char bearer_token[1024];
int ret; int ret;
memset(request, 0, sizeof(struct spotify_request)); memset(request, 0, sizeof(struct spotify_request));
if (check_token && (0 > spotifywebapi_token_refresh(NULL))) if (0 > spotifywebapi_token_refresh(NULL))
{ {
return -1; return -1;
} }
@ -168,7 +165,7 @@ spotifywebapi_request_next(struct spotify_request *request, const char *uri, boo
spotifywebapi_request_end(request); spotifywebapi_request_end(request);
} }
ret = request_uri(request, next_uri, true); ret = request_uri(request, next_uri);
free(next_uri); free(next_uri);
if (ret < 0) if (ret < 0)
@ -424,28 +421,6 @@ get_owner_plid_from_uri(const char *uri, char **owner, char **plid)
return 0; return 0;
} }
/*
* Extracts the id from a spotify album/artist/track uri
*
* E. g. album-uri has the following format: spotify:album:[id]
* The id must be freed by the caller.
*/
static int
get_id_from_uri(const char *uri, char **id)
{
char *tmp;
tmp = strrchr(uri, ':');
if (!tmp)
{
return -1;
}
tmp++;
*id = strdup(tmp);
return 0;
}
int int
spotifywebapi_playlisttracks_fetch(struct spotify_request *request, struct spotify_track *track) spotifywebapi_playlisttracks_fetch(struct spotify_request *request, struct spotify_track *track)
{ {
@ -500,7 +475,7 @@ spotifywebapi_playlist_start(struct spotify_request *request, const char *path,
return -1; return -1;
} }
ret = request_uri(request, uri, true); ret = request_uri(request, uri);
if (ret < 0) if (ret < 0)
{ {
free(owner); free(owner);
@ -516,120 +491,8 @@ spotifywebapi_playlist_start(struct spotify_request *request, const char *path,
return 0; return 0;
} }
static int static int
spotifywebapi_track_start(struct spotify_request *request, const char *path, struct spotify_track *track, json_object **jsonimages, int *image_count) request_user_info()
{
char uri[1024];
char *id;
json_object *jsonalbum;
int ret;
memset(track, 0, sizeof(struct spotify_track));
ret = get_id_from_uri(path, &id);
if (ret < 0)
{
DPRINTF(E_LOG, L_SPOTIFY, "Error extracting id from track uri '%s'\n", path);
return -1;
}
ret = snprintf(uri, sizeof(uri), spotify_track_uri, id);
free(id);
if (ret < 0 || ret >= sizeof(uri))
{
DPRINTF(E_LOG, L_SPOTIFY, "Error creating playlist endpoint uri for playlist '%s'\n", path);
return -1;
}
ret = request_uri(request, uri, true);
if (ret < 0)
{
return -1;
}
request->haystack = json_tokener_parse(request->response_body);
parse_metadata_track(request->haystack, track);
if (jsonimages && image_count)
{
if (json_object_object_get_ex(request->haystack, "album", &jsonalbum))
{
if (json_object_object_get_ex(jsonalbum, "images", jsonimages))
{
*image_count = json_object_array_length(*jsonimages);
}
}
}
return 0;
}
static int
spotifywebapi_image_fetch(json_object *jsonimages, int index, struct spotify_image *image)
{
json_object *jsonimage;
memset(image, 0, sizeof(struct spotify_image));
jsonimage = json_object_array_get_idx(jsonimages, index);
if (!jsonimage)
{
return -1;
}
image->url = jparse_str_from_obj(jsonimage, "url");
image->width = jparse_int_from_obj(jsonimage, "width");
image->height = jparse_int_from_obj(jsonimage, "height");
return 0;
}
char *
spotifywebapi_artwork_get(const char *path, int max_w, int max_h)
{
struct spotify_request request;
struct spotify_track track;
json_object *jsonimages;
struct spotify_image image;
int image_count;
int i;
char *artwork_url;
int ret;
artwork_url = NULL;
image_count = 0;
memset(&request, 0, sizeof(struct spotify_request));
ret = spotifywebapi_track_start(&request, path, &track, &jsonimages, &image_count);
if (ret == 0)
{
DPRINTF(E_DBG, L_SPOTIFY, "Got track: '%s' (%s) \n", track.name, track.uri);
// Get an image at least the same size as requested, images are returned with decreasing size
for (i = (image_count - 1); i >= 0 && ret == 0; i--)
{
ret = spotifywebapi_image_fetch(jsonimages, i, &image);
if (ret < 0 || !image.url)
continue;
free(artwork_url);
DPRINTF(E_DBG, L_SPOTIFY, "Got image for album '%s' - '%s': '%s' (%dx%d)\n", track.album_artist, track.album, image.url, image.height, image.width);
artwork_url = strdup(image.url);
if ((max_w < image.width) || (max_h < image.height))
break;
}
}
spotifywebapi_request_end(&request);
return artwork_url;
}
static int
request_user_info(char **user)
{ {
struct spotify_request request; struct spotify_request request;
int ret; int ret;
@ -639,7 +502,7 @@ request_user_info(char **user)
free(spotify_user); free(spotify_user);
spotify_user = NULL; spotify_user = NULL;
ret = request_uri(&request, spotify_me_uri, false); ret = request_uri(&request, spotify_me_uri);
if (ret < 0) if (ret < 0)
{ {
@ -651,9 +514,6 @@ request_user_info(char **user)
spotify_user_country = safe_strdup(jparse_str_from_obj(request.haystack, "country")); spotify_user_country = safe_strdup(jparse_str_from_obj(request.haystack, "country"));
DPRINTF(E_DBG, L_SPOTIFY, "User '%s', country '%s'\n", spotify_user, spotify_user_country); DPRINTF(E_DBG, L_SPOTIFY, "User '%s', country '%s'\n", spotify_user, spotify_user_country);
if (user)
*user = safe_strdup(spotify_user);
} }
spotifywebapi_request_end(&request); spotifywebapi_request_end(&request);
@ -700,7 +560,7 @@ spotifywebapi_oauth_uri_get(const char *redirect_uri)
} }
static int static int
tokens_get(struct keyval *kv, char **user, const char **err) tokens_get(struct keyval *kv, const char **err)
{ {
struct http_client_ctx ctx; struct http_client_ctx ctx;
char *param; char *param;
@ -784,7 +644,7 @@ tokens_get(struct keyval *kv, char **user, const char **err)
if (spotify_refresh_token) if (spotify_refresh_token)
db_admin_set(DB_ADMIN_SPOTIFY_REFRESH_TOKEN, spotify_refresh_token); db_admin_set(DB_ADMIN_SPOTIFY_REFRESH_TOKEN, spotify_refresh_token);
request_user_info(user); request_user_info();
ret = 0; ret = 0;
@ -802,8 +662,6 @@ spotifywebapi_token_get(const char *code, const char *redirect_uri, char **user,
struct keyval kv; struct keyval kv;
int ret; int ret;
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&token_lck));
*err = ""; *err = "";
memset(&kv, 0, sizeof(struct keyval)); memset(&kv, 0, sizeof(struct keyval));
ret = ( (keyval_add(&kv, "grant_type", "authorization_code") == 0) && ret = ( (keyval_add(&kv, "grant_type", "authorization_code") == 0) &&
@ -818,12 +676,14 @@ spotifywebapi_token_get(const char *code, const char *redirect_uri, char **user,
ret = -1; ret = -1;
} }
else else
ret = tokens_get(&kv, user, err); ret = tokens_get(&kv, err);
if (user && ret == 0)
{
*user = safe_strdup(spotify_user);
}
keyval_clear(&kv); keyval_clear(&kv);
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
return ret; return ret;
} }
@ -831,19 +691,13 @@ int
spotifywebapi_token_refresh(char **user) spotifywebapi_token_refresh(char **user)
{ {
struct keyval kv; struct keyval kv;
char *refresh_token = NULL; char *refresh_token;
const char *err; const char *err;
int ret; int ret;
memset(&kv, 0, sizeof(struct keyval));
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&token_lck));
if (token_requested && difftime(time(NULL), token_requested) < expires_in) if (token_requested && difftime(time(NULL), token_requested) < expires_in)
{ {
DPRINTF(E_DBG, L_SPOTIFY, "Spotify token still valid\n"); DPRINTF(E_DBG, L_SPOTIFY, "Spotify token still valid\n");
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
return 0; return 0;
} }
@ -851,11 +705,12 @@ spotifywebapi_token_refresh(char **user)
if (!refresh_token) if (!refresh_token)
{ {
DPRINTF(E_LOG, L_SPOTIFY, "No spotify refresh token found\n"); DPRINTF(E_LOG, L_SPOTIFY, "No spotify refresh token found\n");
goto error; return -1;
} }
DPRINTF(E_DBG, L_SPOTIFY, "Spotify refresh-token: '%s'\n", refresh_token); DPRINTF(E_DBG, L_SPOTIFY, "Spotify refresh-token: '%s'\n", refresh_token);
memset(&kv, 0, sizeof(struct keyval));
ret = ( (keyval_add(&kv, "grant_type", "refresh_token") == 0) && ret = ( (keyval_add(&kv, "grant_type", "refresh_token") == 0) &&
(keyval_add(&kv, "client_id", spotify_client_id) == 0) && (keyval_add(&kv, "client_id", spotify_client_id) == 0) &&
(keyval_add(&kv, "client_secret", spotify_client_secret) == 0) && (keyval_add(&kv, "client_secret", spotify_client_secret) == 0) &&
@ -863,38 +718,18 @@ spotifywebapi_token_refresh(char **user)
if (!ret) if (!ret)
{ {
DPRINTF(E_LOG, L_SPOTIFY, "Add parameters to keyval failed"); DPRINTF(E_LOG, L_SPOTIFY, "Add parameters to keyval failed");
goto error; ret = -1;
} }
else
ret = tokens_get(&kv, &err);
ret = tokens_get(&kv, user, &err); if (user && ret == 0)
{
*user = safe_strdup(spotify_user);
}
free(refresh_token); free(refresh_token);
keyval_clear(&kv); keyval_clear(&kv);
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
return ret; return ret;
error:
free(refresh_token);
keyval_clear(&kv);
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
return -1;
}
int
spotifywebapi_init()
{
CHECK_ERR(L_SPOTIFY, mutex_init(&token_lck));
return 0;
}
int
spotifywebapi_deinit()
{
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&token_lck));
return 0;
} }

View File

@ -82,13 +82,6 @@ struct spotify_playlist
int tracks_count; int tracks_count;
}; };
struct spotify_image
{
const char *url;
int width;
int height;
};
struct spotify_request struct spotify_request
{ {
struct http_client_ctx *ctx; struct http_client_ctx *ctx;
@ -123,12 +116,5 @@ int
spotifywebapi_playlisttracks_fetch(struct spotify_request *request, struct spotify_track *track); spotifywebapi_playlisttracks_fetch(struct spotify_request *request, struct spotify_track *track);
int int
spotifywebapi_playlist_start(struct spotify_request *request, const char *path, struct spotify_playlist *playlist); spotifywebapi_playlist_start(struct spotify_request *request, const char *path, struct spotify_playlist *playlist);
char *
spotifywebapi_artwork_get(const char *path, int max_w, int max_h);
int
spotifywebapi_init(void);
int
spotifywebapi_deinit(void);
#endif /* SRC_SPOTIFY_WEBAPI_H_ */ #endif /* SRC_SPOTIFY_WEBAPI_H_ */