mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 23:55:57 -05:00
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 commit997b4da4ad
and2da993cc7b
This commit is contained in:
parent
2da993cc7b
commit
4802823f3c
@ -42,7 +42,7 @@
|
||||
#include "artwork.h"
|
||||
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
# include "spotify_webapi.h"
|
||||
# include "spotify.h"
|
||||
#endif
|
||||
|
||||
/* 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 -------------------------------- */
|
||||
|
||||
/* 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
|
||||
* TODO Use evbuffer_add_file or evbuffer_read?
|
||||
*
|
||||
@ -778,7 +727,10 @@ source_item_own_get(struct artwork_ctx *ctx)
|
||||
static int
|
||||
source_item_stream_get(struct artwork_ctx *ctx)
|
||||
{
|
||||
struct http_client_ctx client;
|
||||
struct db_queue_item *queue_item;
|
||||
struct keyval *kv;
|
||||
const char *content_type;
|
||||
char *url;
|
||||
char *ext;
|
||||
int len;
|
||||
@ -812,14 +764,34 @@ source_item_stream_get(struct artwork_ctx *ctx)
|
||||
if (ret > 0)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
out_kv:
|
||||
keyval_clear(kv);
|
||||
free(kv);
|
||||
|
||||
out_url:
|
||||
free(url);
|
||||
|
||||
@ -832,11 +804,8 @@ source_item_spotify_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)
|
||||
@ -845,19 +814,15 @@ source_item_spotify_get(struct artwork_ctx *ctx)
|
||||
return ART_E_ERROR;
|
||||
}
|
||||
|
||||
artwork_url = spotifywebapi_artwork_get(ctx->dbmfi->path, ctx->max_w, ctx->max_h);
|
||||
if (!artwork_url)
|
||||
ret = spotify_artwork_get(raw, ctx->dbmfi->path, ctx->max_w, ctx->max_h);
|
||||
if (ret < 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
@ -893,14 +858,12 @@ source_item_spotify_get(struct artwork_ctx *ctx)
|
||||
|
||||
evbuffer_free(evbuf);
|
||||
evbuffer_free(raw);
|
||||
free(artwork_url);
|
||||
|
||||
return content_type;
|
||||
return ART_FMT_JPEG;
|
||||
|
||||
out_free_evbuf:
|
||||
evbuffer_free(evbuf);
|
||||
evbuffer_free(raw);
|
||||
free(artwork_url);
|
||||
|
||||
return ART_E_ERROR;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@
|
||||
#include "artwork.h"
|
||||
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
# include "spotify_webapi.h"
|
||||
# include "spotify.h"
|
||||
#endif
|
||||
|
||||
#include "ffmpeg-compat.h"
|
||||
@ -224,57 +224,6 @@ static struct artwork_source artwork_item_source[] =
|
||||
|
||||
/* -------------------------------- 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
|
||||
* TODO Use evbuffer_add_file or evbuffer_read?
|
||||
*
|
||||
@ -1154,7 +1103,10 @@ source_item_own_get(struct artwork_ctx *ctx)
|
||||
static int
|
||||
source_item_stream_get(struct artwork_ctx *ctx)
|
||||
{
|
||||
struct http_client_ctx client;
|
||||
struct db_queue_item *queue_item;
|
||||
struct keyval *kv;
|
||||
const char *content_type;
|
||||
char *url;
|
||||
char *ext;
|
||||
int len;
|
||||
@ -1188,14 +1140,34 @@ source_item_stream_get(struct artwork_ctx *ctx)
|
||||
if (ret > 0)
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
out_kv:
|
||||
keyval_clear(kv);
|
||||
free(kv);
|
||||
|
||||
out_url:
|
||||
free(url);
|
||||
|
||||
@ -1211,13 +1183,10 @@ source_item_spotify_get(struct artwork_ctx *ctx)
|
||||
AVInputFormat *ifmt;
|
||||
struct evbuffer *raw;
|
||||
struct evbuffer *evbuf;
|
||||
char *artwork_url;
|
||||
int content_type;
|
||||
int target_w;
|
||||
int target_h;
|
||||
int ret;
|
||||
|
||||
artwork_url = NULL;
|
||||
raw = evbuffer_new();
|
||||
evbuf = evbuffer_new();
|
||||
if (!raw || !evbuf)
|
||||
@ -1226,19 +1195,15 @@ source_item_spotify_get(struct artwork_ctx *ctx)
|
||||
return ART_E_ERROR;
|
||||
}
|
||||
|
||||
artwork_url = spotifywebapi_artwork_get(ctx->dbmfi->path, ctx->max_w, ctx->max_h);
|
||||
if (!artwork_url)
|
||||
ret = spotify_artwork_get(raw, ctx->dbmfi->path, ctx->max_w, ctx->max_h);
|
||||
if (ret < 0)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
@ -1313,7 +1278,7 @@ source_item_spotify_get(struct artwork_ctx *ctx)
|
||||
evbuffer_free(evbuf);
|
||||
evbuffer_free(raw);
|
||||
|
||||
return content_type;
|
||||
return ART_FMT_JPEG;
|
||||
|
||||
out_close_input:
|
||||
avformat_close_input(&src_ctx);
|
||||
|
234
src/spotify.c
234
src/spotify.c
@ -141,6 +141,9 @@ static bool scanning;
|
||||
static pthread_mutex_t status_lck;
|
||||
static struct spotify_status_info spotify_status_info;
|
||||
|
||||
// Timeout timespec
|
||||
static struct timespec spotify_artwork_timeout = { SPOTIFY_ARTWORK_TIMEOUT, 0 };
|
||||
|
||||
// Audio buffer
|
||||
static struct evbuffer *spotify_audio_buffer;
|
||||
|
||||
@ -888,6 +891,194 @@ playback_eot(void *arg, int *retval)
|
||||
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 ------------------------- */
|
||||
/**
|
||||
* This callback is called when an attempt to login has succeeded or failed.
|
||||
@ -1258,6 +1449,41 @@ spotify_playback_seek(int ms)
|
||||
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 */
|
||||
void
|
||||
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, 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 */
|
||||
ret = pthread_create(&tid_spotify, NULL, spotify, NULL);
|
||||
@ -1989,8 +2214,6 @@ spotify_init(void)
|
||||
return 0;
|
||||
|
||||
thread_fail:
|
||||
spotifywebapi_deinit();
|
||||
|
||||
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&status_lck));
|
||||
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&login_cond));
|
||||
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_mutex_destroy(&login_lck));
|
||||
|
||||
/* Deinit web api */
|
||||
spotifywebapi_deinit();
|
||||
|
||||
/* Free audio buffer */
|
||||
evbuffer_free(spotify_audio_buffer);
|
||||
|
||||
|
@ -39,6 +39,9 @@ spotify_playback_stop_nonblock(void);
|
||||
int
|
||||
spotify_playback_seek(int ms);
|
||||
|
||||
int
|
||||
spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h);
|
||||
|
||||
void
|
||||
spotify_oauth_interface(struct evbuffer *evbuf, const char *redirect_uri);
|
||||
|
||||
|
@ -43,15 +43,12 @@ static char *spotify_user;
|
||||
static int32_t expires_in = 3600;
|
||||
static time_t token_requested = 0;
|
||||
|
||||
static pthread_mutex_t token_lck;
|
||||
|
||||
// Endpoints and credentials for the web api
|
||||
static const char *spotify_client_id = "0e684a5422384114a8ae7ac020f01789";
|
||||
static const char *spotify_client_secret = "232af95f39014c9ba218285a5c11a239";
|
||||
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_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";
|
||||
|
||||
|
||||
@ -75,14 +72,14 @@ free_http_client_ctx(struct http_client_ctx *ctx)
|
||||
}
|
||||
|
||||
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];
|
||||
int ret;
|
||||
|
||||
memset(request, 0, sizeof(struct spotify_request));
|
||||
|
||||
if (check_token && (0 > spotifywebapi_token_refresh(NULL)))
|
||||
if (0 > spotifywebapi_token_refresh(NULL))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@ -168,7 +165,7 @@ spotifywebapi_request_next(struct spotify_request *request, const char *uri, boo
|
||||
spotifywebapi_request_end(request);
|
||||
}
|
||||
|
||||
ret = request_uri(request, next_uri, true);
|
||||
ret = request_uri(request, next_uri);
|
||||
free(next_uri);
|
||||
|
||||
if (ret < 0)
|
||||
@ -424,28 +421,6 @@ get_owner_plid_from_uri(const char *uri, char **owner, char **plid)
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
ret = request_uri(request, uri, true);
|
||||
ret = request_uri(request, uri);
|
||||
if (ret < 0)
|
||||
{
|
||||
free(owner);
|
||||
@ -516,120 +491,8 @@ spotifywebapi_playlist_start(struct spotify_request *request, const char *path,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
spotifywebapi_track_start(struct spotify_request *request, const char *path, struct spotify_track *track, json_object **jsonimages, int *image_count)
|
||||
{
|
||||
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)
|
||||
request_user_info()
|
||||
{
|
||||
struct spotify_request request;
|
||||
int ret;
|
||||
@ -639,7 +502,7 @@ request_user_info(char **user)
|
||||
free(spotify_user);
|
||||
spotify_user = NULL;
|
||||
|
||||
ret = request_uri(&request, spotify_me_uri, false);
|
||||
ret = request_uri(&request, spotify_me_uri);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
@ -651,9 +514,6 @@ request_user_info(char **user)
|
||||
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);
|
||||
|
||||
if (user)
|
||||
*user = safe_strdup(spotify_user);
|
||||
}
|
||||
|
||||
spotifywebapi_request_end(&request);
|
||||
@ -700,7 +560,7 @@ spotifywebapi_oauth_uri_get(const char *redirect_uri)
|
||||
}
|
||||
|
||||
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;
|
||||
char *param;
|
||||
@ -784,7 +644,7 @@ tokens_get(struct keyval *kv, char **user, const char **err)
|
||||
if (spotify_refresh_token)
|
||||
db_admin_set(DB_ADMIN_SPOTIFY_REFRESH_TOKEN, spotify_refresh_token);
|
||||
|
||||
request_user_info(user);
|
||||
request_user_info();
|
||||
|
||||
ret = 0;
|
||||
|
||||
@ -802,8 +662,6 @@ spotifywebapi_token_get(const char *code, const char *redirect_uri, char **user,
|
||||
struct keyval kv;
|
||||
int ret;
|
||||
|
||||
CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&token_lck));
|
||||
|
||||
*err = "";
|
||||
memset(&kv, 0, sizeof(struct keyval));
|
||||
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;
|
||||
}
|
||||
else
|
||||
ret = tokens_get(&kv, user, err);
|
||||
ret = tokens_get(&kv, err);
|
||||
|
||||
if (user && ret == 0)
|
||||
{
|
||||
*user = safe_strdup(spotify_user);
|
||||
}
|
||||
keyval_clear(&kv);
|
||||
|
||||
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -831,19 +691,13 @@ int
|
||||
spotifywebapi_token_refresh(char **user)
|
||||
{
|
||||
struct keyval kv;
|
||||
char *refresh_token = NULL;
|
||||
char *refresh_token;
|
||||
const char *err;
|
||||
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)
|
||||
{
|
||||
DPRINTF(E_DBG, L_SPOTIFY, "Spotify token still valid\n");
|
||||
|
||||
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -851,11 +705,12 @@ spotifywebapi_token_refresh(char **user)
|
||||
if (!refresh_token)
|
||||
{
|
||||
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);
|
||||
|
||||
memset(&kv, 0, sizeof(struct keyval));
|
||||
ret = ( (keyval_add(&kv, "grant_type", "refresh_token") == 0) &&
|
||||
(keyval_add(&kv, "client_id", spotify_client_id) == 0) &&
|
||||
(keyval_add(&kv, "client_secret", spotify_client_secret) == 0) &&
|
||||
@ -863,38 +718,18 @@ spotifywebapi_token_refresh(char **user)
|
||||
if (!ret)
|
||||
{
|
||||
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);
|
||||
keyval_clear(&kv);
|
||||
|
||||
CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&token_lck));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -82,13 +82,6 @@ struct spotify_playlist
|
||||
int tracks_count;
|
||||
};
|
||||
|
||||
struct spotify_image
|
||||
{
|
||||
const char *url;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct spotify_request
|
||||
{
|
||||
struct http_client_ctx *ctx;
|
||||
@ -123,12 +116,5 @@ int
|
||||
spotifywebapi_playlisttracks_fetch(struct spotify_request *request, struct spotify_track *track);
|
||||
int
|
||||
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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user