From c47287a556d9c28a57074ec44c9277b3da9e1f19 Mon Sep 17 00:00:00 2001 From: ejurgensen Date: Fri, 4 Apr 2014 23:14:43 +0200 Subject: [PATCH] Support for Spotify artwork --- src/artwork.c | 16 ++++- src/spotify.c | 180 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/spotify.h | 3 + 3 files changed, 192 insertions(+), 7 deletions(-) diff --git a/src/artwork.c b/src/artwork.c index 05ccd61a..b6cf4729 100644 --- a/src/artwork.c +++ b/src/artwork.c @@ -43,6 +43,9 @@ #endif #include "artwork.h" +#ifdef HAVE_SPOTIFY_H +# include "spotify.h" +#endif 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) return -1; - /* If Spotify item don't look for artwork */ 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); if ((ret < 0) || (ret >= sizeof(artwork))) diff --git a/src/spotify.c b/src/spotify.c index 2770efbc..c848fe62 100644 --- a/src/spotify.c +++ b/src/spotify.c @@ -80,6 +80,14 @@ struct audio_get_param int wanted; }; +struct artwork_get_param +{ + struct evbuffer *evbuf; + char *path; + int max_w; + int max_h; +}; + struct spotify_command; typedef int (*cmd_func)(struct spotify_command *cmd); @@ -98,7 +106,8 @@ struct spotify_command void *noarg; sp_link *link; int seek_ms; - struct audio_get_param agp; + struct audio_get_param audio; + struct artwork_get_param artwork; } arg; 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 int (*fptr_sp_album_year_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 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 */ 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_year_t fptr_sp_album_year; 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_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 */ static int fptr_assign_all() @@ -314,7 +339,14 @@ fptr_assign_all() && (fptr_sp_album_artist = dlsym(h, "sp_album_artist")) && (fptr_sp_album_year = dlsym(h, "sp_album_year")) && (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_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(); @@ -934,7 +966,7 @@ audio_get(struct spotify_command *cmd) 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 ((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; - ret = evbuffer_add(cmd->arg.agp.evbuf, afd->samples, s); + ret = evbuffer_add(cmd->arg.audio.evbuf, afd->samples, s); free(afd); afd = NULL; if (ret < 0) @@ -989,6 +1021,122 @@ audio_get(struct spotify_command *cmd) 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 ------------------------- */ /** @@ -1468,8 +1616,30 @@ spotify_audio_get(struct evbuffer *evbuf, int wanted) command_init(&cmd); cmd.func = audio_get; - cmd.arg.agp.evbuf = evbuf; - cmd.arg.agp.wanted = wanted; + cmd.arg.audio.evbuf = evbuf; + 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); diff --git a/src/spotify.h b/src/spotify.h index 0c9658c9..72a3dcc9 100644 --- a/src/spotify.h +++ b/src/spotify.h @@ -23,6 +23,9 @@ spotify_playback_seek(int ms); int spotify_audio_get(struct evbuffer *evbuf, int wanted); +int +spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h); + void spotify_login(char *path);