mirror of
https://github.com/owntone/owntone-server.git
synced 2025-03-20 04:24:20 -04:00
Support for Spotify artwork
This commit is contained in:
parent
1be543b00a
commit
c47287a556
@ -43,6 +43,9 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "artwork.h"
|
#include "artwork.h"
|
||||||
|
|
||||||
|
#ifdef HAVE_SPOTIFY_H
|
||||||
|
# include "spotify.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char *cover_extension[] =
|
static const char *cover_extension[] =
|
||||||
{
|
{
|
||||||
@ -856,9 +859,18 @@ artwork_get_own_image(char *path, int max_w, int max_h, int format, struct evbuf
|
|||||||
if (strncmp(path, "http://", strlen("http://")) == 0)
|
if (strncmp(path, "http://", strlen("http://")) == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* If Spotify item don't look for artwork */
|
|
||||||
if (strncmp(path, "spotify:", strlen("spotify:")) == 0)
|
if (strncmp(path, "spotify:", strlen("spotify:")) == 0)
|
||||||
return -1;
|
{
|
||||||
|
if (!(format & ART_CAN_JPEG))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ret = spotify_artwork_get(evbuf, path, max_w, max_h);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
else
|
||||||
|
return ART_FMT_JPEG;
|
||||||
|
}
|
||||||
|
|
||||||
ret = snprintf(artwork, sizeof(artwork), "%s", path);
|
ret = snprintf(artwork, sizeof(artwork), "%s", path);
|
||||||
if ((ret < 0) || (ret >= sizeof(artwork)))
|
if ((ret < 0) || (ret >= sizeof(artwork)))
|
||||||
|
180
src/spotify.c
180
src/spotify.c
@ -80,6 +80,14 @@ struct audio_get_param
|
|||||||
int wanted;
|
int wanted;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct artwork_get_param
|
||||||
|
{
|
||||||
|
struct evbuffer *evbuf;
|
||||||
|
char *path;
|
||||||
|
int max_w;
|
||||||
|
int max_h;
|
||||||
|
};
|
||||||
|
|
||||||
struct spotify_command;
|
struct spotify_command;
|
||||||
|
|
||||||
typedef int (*cmd_func)(struct spotify_command *cmd);
|
typedef int (*cmd_func)(struct spotify_command *cmd);
|
||||||
@ -98,7 +106,8 @@ struct spotify_command
|
|||||||
void *noarg;
|
void *noarg;
|
||||||
sp_link *link;
|
sp_link *link;
|
||||||
int seek_ms;
|
int seek_ms;
|
||||||
struct audio_get_param agp;
|
struct audio_get_param audio;
|
||||||
|
struct artwork_get_param artwork;
|
||||||
} arg;
|
} arg;
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
@ -208,9 +217,17 @@ typedef const char* (*fptr_sp_album_name_t)(sp_album *album);
|
|||||||
typedef sp_artist* (*fptr_sp_album_artist_t)(sp_album *album);
|
typedef sp_artist* (*fptr_sp_album_artist_t)(sp_album *album);
|
||||||
typedef int (*fptr_sp_album_year_t)(sp_album *album);
|
typedef int (*fptr_sp_album_year_t)(sp_album *album);
|
||||||
typedef sp_albumtype (*fptr_sp_album_type_t)(sp_album *album);
|
typedef sp_albumtype (*fptr_sp_album_type_t)(sp_album *album);
|
||||||
|
typedef const byte* (*fptr_sp_album_cover_t)(sp_album *album, sp_image_size size);
|
||||||
|
|
||||||
typedef const char* (*fptr_sp_artist_name_t)(sp_artist *artist);
|
typedef const char* (*fptr_sp_artist_name_t)(sp_artist *artist);
|
||||||
|
|
||||||
|
typedef sp_image* (*fptr_sp_image_create_t)(sp_session *session, const byte image_id[20]);
|
||||||
|
typedef bool (*fptr_sp_image_is_loaded_t)(sp_image *image);
|
||||||
|
typedef sp_error (*fptr_sp_image_error_t)(sp_image *image);
|
||||||
|
typedef sp_imageformat (*fptr_sp_image_format_t)(sp_image *image);
|
||||||
|
typedef const void* (*fptr_sp_image_data_t)(sp_image *image, size_t *data_size);
|
||||||
|
typedef sp_error (*fptr_sp_image_release_t)(sp_image *image);
|
||||||
|
|
||||||
/* Define actual function pointers */
|
/* Define actual function pointers */
|
||||||
fptr_sp_error_message_t fptr_sp_error_message;
|
fptr_sp_error_message_t fptr_sp_error_message;
|
||||||
|
|
||||||
@ -259,9 +276,17 @@ fptr_sp_album_name_t fptr_sp_album_name;
|
|||||||
fptr_sp_album_artist_t fptr_sp_album_artist;
|
fptr_sp_album_artist_t fptr_sp_album_artist;
|
||||||
fptr_sp_album_year_t fptr_sp_album_year;
|
fptr_sp_album_year_t fptr_sp_album_year;
|
||||||
fptr_sp_album_type_t fptr_sp_album_type;
|
fptr_sp_album_type_t fptr_sp_album_type;
|
||||||
|
fptr_sp_album_cover_t fptr_sp_album_cover;
|
||||||
|
|
||||||
fptr_sp_artist_name_t fptr_sp_artist_name;
|
fptr_sp_artist_name_t fptr_sp_artist_name;
|
||||||
|
|
||||||
|
fptr_sp_image_create_t fptr_sp_image_create;
|
||||||
|
fptr_sp_image_is_loaded_t fptr_sp_image_is_loaded;
|
||||||
|
fptr_sp_image_error_t fptr_sp_image_error;
|
||||||
|
fptr_sp_image_format_t fptr_sp_image_format;
|
||||||
|
fptr_sp_image_data_t fptr_sp_image_data;
|
||||||
|
fptr_sp_image_release_t fptr_sp_image_release;
|
||||||
|
|
||||||
/* Assign function pointers to libspotify symbol */
|
/* Assign function pointers to libspotify symbol */
|
||||||
static int
|
static int
|
||||||
fptr_assign_all()
|
fptr_assign_all()
|
||||||
@ -314,7 +339,14 @@ fptr_assign_all()
|
|||||||
&& (fptr_sp_album_artist = dlsym(h, "sp_album_artist"))
|
&& (fptr_sp_album_artist = dlsym(h, "sp_album_artist"))
|
||||||
&& (fptr_sp_album_year = dlsym(h, "sp_album_year"))
|
&& (fptr_sp_album_year = dlsym(h, "sp_album_year"))
|
||||||
&& (fptr_sp_album_type = dlsym(h, "sp_album_type"))
|
&& (fptr_sp_album_type = dlsym(h, "sp_album_type"))
|
||||||
|
&& (fptr_sp_album_cover = dlsym(h, "sp_album_cover"))
|
||||||
&& (fptr_sp_artist_name = dlsym(h, "sp_artist_name"))
|
&& (fptr_sp_artist_name = dlsym(h, "sp_artist_name"))
|
||||||
|
&& (fptr_sp_image_create = dlsym(h, "sp_image_create"))
|
||||||
|
&& (fptr_sp_image_is_loaded = dlsym(h, "sp_image_is_loaded"))
|
||||||
|
&& (fptr_sp_image_error = dlsym(h, "sp_image_error"))
|
||||||
|
&& (fptr_sp_image_format = dlsym(h, "sp_image_format"))
|
||||||
|
&& (fptr_sp_image_data = dlsym(h, "sp_image_data"))
|
||||||
|
&& (fptr_sp_image_release = dlsym(h, "sp_image_release"))
|
||||||
;
|
;
|
||||||
|
|
||||||
err = dlerror();
|
err = dlerror();
|
||||||
@ -934,7 +966,7 @@ audio_get(struct spotify_command *cmd)
|
|||||||
|
|
||||||
pthread_mutex_lock(&g_audio_fifo->mutex);
|
pthread_mutex_lock(&g_audio_fifo->mutex);
|
||||||
|
|
||||||
while ((processed < cmd->arg.agp.wanted) && (g_state != SPOTIFY_STATE_STOPPED))
|
while ((processed < cmd->arg.audio.wanted) && (g_state != SPOTIFY_STATE_STOPPED))
|
||||||
{
|
{
|
||||||
// If track has ended and buffer is empty
|
// If track has ended and buffer is empty
|
||||||
if ((g_state == SPOTIFY_STATE_STOPPING) && (g_audio_fifo->qlen <= 0))
|
if ((g_state == SPOTIFY_STATE_STOPPING) && (g_audio_fifo->qlen <= 0))
|
||||||
@ -971,7 +1003,7 @@ audio_get(struct spotify_command *cmd)
|
|||||||
|
|
||||||
s = afd->nsamples * sizeof(int16_t) * 2;
|
s = afd->nsamples * sizeof(int16_t) * 2;
|
||||||
|
|
||||||
ret = evbuffer_add(cmd->arg.agp.evbuf, afd->samples, s);
|
ret = evbuffer_add(cmd->arg.audio.evbuf, afd->samples, s);
|
||||||
free(afd);
|
free(afd);
|
||||||
afd = NULL;
|
afd = NULL;
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -989,6 +1021,122 @@ audio_get(struct spotify_command *cmd)
|
|||||||
return processed;
|
return processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
artwork_get(struct spotify_command *cmd)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
sp_link *link;
|
||||||
|
sp_track *track;
|
||||||
|
sp_album *album;
|
||||||
|
const byte *image_id;
|
||||||
|
sp_image *image;
|
||||||
|
sp_image_size image_size;
|
||||||
|
sp_imageformat imageformat;
|
||||||
|
sp_error err;
|
||||||
|
const void *data;
|
||||||
|
size_t data_size;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
path = cmd->arg.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO rescale
|
||||||
|
image_size = SP_IMAGE_SIZE_SMALL; // 64x64
|
||||||
|
if ((cmd->arg.artwork.max_w > 200) && (cmd->arg.artwork.max_h > 200))
|
||||||
|
image_size = SP_IMAGE_SIZE_NORMAL; // 300x300
|
||||||
|
if ((cmd->arg.artwork.max_w > 500) && (cmd->arg.artwork.max_h > 500))
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We want to be fast, so no waiting for the image to load
|
||||||
|
if (!fptr_sp_image_is_loaded(image))
|
||||||
|
goto level3_exit;
|
||||||
|
|
||||||
|
err = fptr_sp_image_error(image);
|
||||||
|
if (err != SP_ERROR_OK)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_SPOTIFY, "Getting artwork failed, Spotify error: %s\n", fptr_sp_error_message(err));
|
||||||
|
goto level3_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 level3_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = fptr_sp_image_data(image, &data_size);
|
||||||
|
if (!data || (data_size == 0))
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_SPOTIFY, "Getting artwork failed, no image data from Spotify: %s\n", path);
|
||||||
|
goto level3_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = evbuffer_expand(cmd->arg.artwork.evbuf, data_size);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_SPOTIFY, "Out of memory for artwork\n");
|
||||||
|
goto level3_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = evbuffer_add(cmd->arg.artwork.evbuf, data, data_size);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_SPOTIFY, "Could not add Spotify image to event buffer\n");
|
||||||
|
goto level3_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(E_DBG, L_SPOTIFY, "Spotify artwork loaded ok\n");
|
||||||
|
|
||||||
|
fptr_sp_image_release(image);
|
||||||
|
|
||||||
|
return data_size;
|
||||||
|
|
||||||
|
level3_exit:
|
||||||
|
fptr_sp_image_release(image);
|
||||||
|
|
||||||
|
level2_exit:
|
||||||
|
fptr_sp_link_release(link);
|
||||||
|
|
||||||
|
level1_exit:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* --------------------------- SESSION CALLBACKS ------------------------- */
|
/* --------------------------- SESSION CALLBACKS ------------------------- */
|
||||||
/**
|
/**
|
||||||
@ -1468,8 +1616,30 @@ spotify_audio_get(struct evbuffer *evbuf, int wanted)
|
|||||||
command_init(&cmd);
|
command_init(&cmd);
|
||||||
|
|
||||||
cmd.func = audio_get;
|
cmd.func = audio_get;
|
||||||
cmd.arg.agp.evbuf = evbuf;
|
cmd.arg.audio.evbuf = evbuf;
|
||||||
cmd.arg.agp.wanted = wanted;
|
cmd.arg.audio.wanted = wanted;
|
||||||
|
|
||||||
|
ret = sync_command(&cmd);
|
||||||
|
|
||||||
|
command_deinit(&cmd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thread: httpd (artwork) */
|
||||||
|
int
|
||||||
|
spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h)
|
||||||
|
{
|
||||||
|
struct spotify_command cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
command_init(&cmd);
|
||||||
|
|
||||||
|
cmd.func = artwork_get;
|
||||||
|
cmd.arg.artwork.evbuf = evbuf;
|
||||||
|
cmd.arg.artwork.path = path;
|
||||||
|
cmd.arg.artwork.max_w = max_w;
|
||||||
|
cmd.arg.artwork.max_h = max_h;
|
||||||
|
|
||||||
ret = sync_command(&cmd);
|
ret = sync_command(&cmd);
|
||||||
|
|
||||||
|
@ -23,6 +23,9 @@ spotify_playback_seek(int ms);
|
|||||||
int
|
int
|
||||||
spotify_audio_get(struct evbuffer *evbuf, int wanted);
|
spotify_audio_get(struct evbuffer *evbuf, int wanted);
|
||||||
|
|
||||||
|
int
|
||||||
|
spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h);
|
||||||
|
|
||||||
void
|
void
|
||||||
spotify_login(char *path);
|
spotify_login(char *path);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user