mirror of
https://github.com/owntone/owntone-server.git
synced 2025-02-05 02:38:09 -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 commit 997b4da4ad8edf299f2437f49f0a8e7337cfa658 and 2da993cc7b49dba28ae7e5db461aee973919e6c9
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…
x
Reference in New Issue
Block a user