diff --git a/Makefile.am b/Makefile.am
index 3e39e3b4..eaa0781b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
-if COND_LIBRESPOTC
+if COND_SPOTIFY
LIBRESPOTC_SUBDIR=src/inputs/librespot-c
endif
@@ -65,9 +65,6 @@ if COND_INSTALL_USER
( ! $(GETENT) group pulse-access &> /dev/null || $(USERMOD) --append --groups pulse-access $(OWNTONE_USER) )
$(CHOWN) $(OWNTONE_USER).$(OWNTONE_GROUP) "$(DESTDIR)$(localstatedir)/cache/$(PACKAGE)"
endif
-if COND_LIBSPOTIFY
- $(MKDIR_P) "$(DESTDIR)$(localstatedir)/cache/$(PACKAGE)/libspotify"
-endif
# we do this manually instead of using sysconf_DATA because it overwrites existing config
if COND_INSTALL_CONF_FILE
$(MKDIR_P) "$(DESTDIR)$(sysconfdir)"
diff --git a/configure.ac b/configure.ac
index 7e5d238c..9deadc69 100644
--- a/configure.ac
+++ b/configure.ac
@@ -282,25 +282,7 @@ OWNTONE_ARG_DISABLE([Spotify support], [spotify], [SPOTIFY_LIBRESPOTC],
AC_DEFINE([SPOTIFY], 1,
[Define to 1 to enable Spotify])
])
-AM_CONDITIONAL([COND_LIBRESPOTC], [[test "x$enable_spotify" = "xyes"]])
-
-dnl Spotify with dynamic linking to libspotify (legacy)
-OWNTONE_ARG_ENABLE([legacy libspotify support], [libspotify], [SPOTIFY_LIBSPOTIFY],
- [AS_IF([[test "x$with_libevent_pthreads" = "xno"]],
- [AC_MSG_ERROR([[libspotify support requires libevent_pthreads]])])
- OWNTONE_MODULES_CHECK([SPOTIFY_LIBSPOTIFY], [LIBSPOTIFY], [libspotify],
- [], [libspotify/api.h])
- dnl Don't link with libspotify, use dynamic linking
- AC_SEARCH_LIBS([dlopen], [dl], [],
- [AC_MSG_ERROR([[libspotify support requires dlopen]])])
- OWNTONE_VAR_PREPEND([OWNTONE_OPTS_CPPFLAGS], [$SPOTIFY_LIBSPOTIFY_CPPFLAGS])
- OWNTONE_VAR_PREPEND([OWNTONE_OPTS_LIBS], [-rdynamic])
- AC_DEFINE([SPOTIFY], 1,
- [Define to 1 to enable Spotify])
- ])
-AM_CONDITIONAL([COND_LIBSPOTIFY], [[test "x$enable_libspotify" = "xyes"]])
-
-AM_CONDITIONAL([COND_SPOTIFY], [[test "x$enable_spotify" = "xyes" -o "x$enable_libspotify" = "xyes"]])
+AM_CONDITIONAL([COND_SPOTIFY], [[test "x$enable_spotify" = "xyes"]])
dnl LastFM support
OWNTONE_ARG_DISABLE([LastFM support], [lastfm], [LASTFM])
diff --git a/docs/installation.md b/docs/installation.md
index 1180303e..556fa89f 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -38,7 +38,6 @@ argument when you run ./configure:
Feature | Configure argument | Packages
---------------------|--------------------------|-------------------------------------
Chromecast | `--enable-chromecast` | libgnutls*-dev
- Legacy libspotify | `--enable-libspotify` | libspotify-dev
Pulseaudio | `--with-pulseaudio` | libpulse-dev
These features can be disabled saving you package dependencies:
@@ -258,8 +257,6 @@ Libraries:
often already installed as part of your distro
- libpulse (optional - Pulseaudio local audio)
from
-- libspotify (optional - Spotify support)
- (deprecated by Spotify)
- libgnutls (optional - Chromecast support)
from
- libwebsockets 2.0.2+ (optional - websocket support)
@@ -282,15 +279,6 @@ generate the configure script and `Makefile.in`.
To display the configure options `run ./configure --help`.
Support for Spotify is optional. Use `--disable-spotify` to disable this feature.
-OwnTone supports two ways of integrating with Spotify: Using its own, built-in
-integration layer (which is the default), or to use Spotify's deprecated
-libspotify. To enable the latter, you must configure with `--enable-libspotify`
-and also make sure libspotify's `libspotify/api.h` is installed at compile time.
-At runtime, libspotify must be installed, and `use_libspotify` must be enabled
-in owntone.conf. OwnTone uses runtime dynamic linking to the libspotify library,
-so even though you compiled with `--enable-libspotify`, the executable will
-still be able to run on systems without libspotify. If you only want libspotify
-integration, you can use `--disable-spotify` and `--enable-libspotify`.
Support for LastFM scrobbling is optional. Use `--enable-lastfm` to enable this
feature.
diff --git a/docs/integrations/spotify.md b/docs/integrations/spotify.md
index c14b4a5f..f0e20b71 100644
--- a/docs/integrations/spotify.md
+++ b/docs/integrations/spotify.md
@@ -52,10 +52,3 @@ pipe = "/srv/music/spotify"
# Output metadata in Shairport Sync format (https://github.com/mikebrady/shairport-sync-metadata-reader)
metadataPipe = "/srv/music/spotify.metadata"
```
-
-## Via libspotify
-
-This method is being deprecated, but is still available if the server was built
-with it, libspotify is installed and `use_libspotify` is enabled in the config
-file. Please consult [previous README versions](#references) for details on
-using libspotify.
diff --git a/owntone.conf.in b/owntone.conf.in
index 38f59ebe..31b530fd 100644
--- a/owntone.conf.in
+++ b/owntone.conf.in
@@ -352,18 +352,6 @@ audio {
# Spotify settings (only have effect if Spotify enabled - see README/INSTALL)
spotify {
- # The server can stream from Spotify using either its own implementation
- # or using Spotify's libspotify (which was deprecated many years ago)
-# use_libspotify = false
-
- # Directory where user settings should be stored (credentials)
- # (only has effect with libspotify)
-# settings_dir = "@localstatedir@/cache/@PACKAGE@/libspotify"
-
- # Cache directory
- # (only has effect with libspotify)
-# cache_dir = "/tmp"
-
# Set preferred bitrate for music streaming
# 0: No preference (default), 1: 96kbps, 2: 160kbps, 3: 320kbps
# bitrate = 0
diff --git a/src/Makefile.am b/src/Makefile.am
index f904f564..fa01a0a9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -3,16 +3,8 @@ sbin_PROGRAMS = owntone
if COND_SPOTIFY
SPOTIFY_SRC = \
- library/spotify_webapi.c library/spotify_webapi.h inputs/spotify.c inputs/spotify.h
-endif
-if COND_LIBRESPOTC
-LIBRESPOTC_SRC = \
- inputs/spotify_librespotc.c
-endif
-if COND_LIBSPOTIFY
-LIBSPOTIFY_SRC = \
- inputs/spotify_libspotify.c \
- inputs/libspotify/libspotify.c inputs/libspotify/libspotify.h
+ library/spotify_webapi.c library/spotify_webapi.h \
+ inputs/spotify.c inputs/spotify.h inputs/spotify_librespotc.c
endif
if COND_LASTFM
@@ -128,7 +120,7 @@ owntone_SOURCES = main.c \
outputs/streaming.c outputs/dummy.c outputs/fifo.c outputs/rcp.c \
$(ALSA_SRC) $(PULSEAUDIO_SRC) $(CHROMECAST_SRC) \
evrtsp/rtsp.c evrtsp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \
- $(SPOTIFY_SRC) $(LIBRESPOTC_SRC) $(LIBSPOTIFY_SRC) \
+ $(SPOTIFY_SRC) \
$(LASTFM_SRC) \
$(MPD_SRC) \
listener.c listener.h \
diff --git a/src/conffile.c b/src/conffile.c
index 97d67e38..59af0e5f 100644
--- a/src/conffile.c
+++ b/src/conffile.c
@@ -198,9 +198,9 @@ static cfg_opt_t sec_rcp[] =
/* Spotify section structure */
static cfg_opt_t sec_spotify[] =
{
- CFG_BOOL("use_libspotify", cfg_false, CFGF_NONE),
- CFG_STR("settings_dir", STATEDIR "/cache/" PACKAGE "/libspotify", CFGF_NONE),
- CFG_STR("cache_dir", "/tmp", CFGF_NONE),
+ CFG_BOOL("use_libspotify", cfg_false, CFGF_DEPRECATED),
+ CFG_STR("settings_dir", STATEDIR "/cache/" PACKAGE "/libspotify", CFGF_DEPRECATED),
+ CFG_STR("cache_dir", "/tmp", CFGF_DEPRECATED),
CFG_INT("bitrate", 0, CFGF_NONE),
CFG_BOOL("base_playlist_disable", cfg_false, CFGF_NONE),
CFG_BOOL("artist_override", cfg_false, CFGF_NONE),
diff --git a/src/httpd_jsonapi.c b/src/httpd_jsonapi.c
index f2050ac7..9239088d 100644
--- a/src/httpd_jsonapi.c
+++ b/src/httpd_jsonapi.c
@@ -867,8 +867,8 @@ jsonapi_reply_config(struct httpd_request *hreq)
json_object_object_add(jreply, "allow_modifying_stored_playlists", json_object_new_boolean(allow_modifying_stored_playlists));
safe_json_add_string(jreply, "default_playlist_directory", default_playlist_directory);
- // Wether libspotify should be used instead of librespot
- json_object_object_add(jreply, "use_libspotify", json_object_new_boolean(cfg_getbool(cfg_getsec(cfg, "spotify"), "use_libspotify")));
+ // libspotify is sunset, so always return false. TODO: remove this parameter.
+ json_object_object_add(jreply, "use_libspotify", json_object_new_boolean(false));
CHECK_ERRNO(L_WEB, evbuffer_add_printf(hreq->reply, "%s", json_object_to_json_string(jreply)));
diff --git a/src/inputs/libspotify/libspotify.c b/src/inputs/libspotify/libspotify.c
deleted file mode 100644
index 7f418751..00000000
--- a/src/inputs/libspotify/libspotify.c
+++ /dev/null
@@ -1,1654 +0,0 @@
-/*
- * Copyright (C) 2016-17 Espen Jürgensen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifdef HAVE_CONFIG_H
-# include
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#include "libspotify.h"
-#include "library.h"
-#include "library/spotify_webapi.h"
-#include "logger.h"
-#include "misc.h"
-#include "http.h"
-#include "conffile.h"
-#include "cache.h"
-#include "commands.h"
-#include "input.h"
-#include "listener.h"
-
-
-// How long to wait for artwork (in sec) before giving up
-#define SPOTIFY_ARTWORK_TIMEOUT 3
-// An upper limit on sequential requests to Spotify's web api
-// - each request will return 50 objects (tracks)
-#define SPOTIFY_WEB_REQUESTS_MAX 20
-
-/* --- Types --- */
-enum spotify_state
-{
- SPOTIFY_STATE_INACTIVE,
- SPOTIFY_STATE_WAIT,
- SPOTIFY_STATE_PLAYING,
- SPOTIFY_STATE_PAUSED,
- SPOTIFY_STATE_STOPPING,
- SPOTIFY_STATE_STOPPED,
-};
-
-struct artwork_get_param
-{
- struct evbuffer *evbuf;
- char *path;
- int max_w;
- int max_h;
-
- sp_image *image;
- pthread_mutex_t mutex;
- pthread_cond_t cond;
- int is_loaded;
-};
-
-static void
-libspotify_playback_stop_nonblock(void);
-
-/* --- Globals --- */
-// Spotify thread
-static pthread_t tid_spotify;
-
-// Used to make sure no login is attempted before the logout cb from Spotify
-static pthread_mutex_t login_lck;
-static pthread_cond_t login_cond;
-
-// Event base, pipes and events
-struct event_base *evbase_spotify;
-static int g_notify_pipe[2];
-static struct event *g_notifyev;
-
-static struct commands_base *cmdbase;
-
-// The session handle
-static sp_session *g_sess;
-// The library handle
-static void *g_libhandle;
-// The state telling us what the thread is currently doing
-static enum spotify_state g_state;
-
-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;
-
-/**
- * The application key is specific to forked-daapd, and allows Spotify
- * to produce statistics on how their service is used.
- */
-const uint8_t g_appkey[] = {
- 0x01, 0xC6, 0x9D, 0x18, 0xA4, 0xF7, 0x79, 0x12, 0x43, 0x55, 0x0F, 0xAD, 0xBF, 0x23, 0x23, 0x10,
- 0x2E, 0x51, 0x46, 0x8F, 0x06, 0x3D, 0xEE, 0xC3, 0xF0, 0x2A, 0x5D, 0x8E, 0x72, 0x35, 0xD1, 0x21,
- 0x44, 0xE3, 0x19, 0x80, 0xED, 0xD5, 0xAD, 0xE6, 0xE1, 0xDD, 0xBE, 0xCB, 0xA9, 0x84, 0xBD, 0xC2,
- 0xAF, 0xB1, 0xF2, 0xD5, 0x87, 0xFC, 0x35, 0xD6, 0x1C, 0x5F, 0x5B, 0x76, 0x38, 0x1D, 0x6E, 0x49,
- 0x6D, 0x85, 0x15, 0xCD, 0x38, 0x14, 0xD6, 0xB8, 0xFE, 0x05, 0x0A, 0xAC, 0x9B, 0x31, 0xD1, 0xC0,
- 0xAF, 0x16, 0x78, 0x48, 0x49, 0x27, 0x41, 0xCA, 0xAF, 0x07, 0xEC, 0x10, 0x5D, 0x19, 0x43, 0x2E,
- 0x84, 0xEB, 0x43, 0x5D, 0x4B, 0xBF, 0xD0, 0x5C, 0xDF, 0x3D, 0x12, 0x6D, 0x1C, 0x76, 0x4E, 0x9F,
- 0xBF, 0x14, 0xC9, 0x46, 0x95, 0x99, 0x32, 0x6A, 0xC2, 0xF1, 0x89, 0xA4, 0xB3, 0xF3, 0xA0, 0xEB,
- 0xDA, 0x84, 0x67, 0x27, 0x07, 0x1F, 0xF6, 0x19, 0xAC, 0xF1, 0xB8, 0xB6, 0xCF, 0xAB, 0xF8, 0x0A,
- 0xEE, 0x4D, 0xAC, 0xC2, 0x39, 0x63, 0x50, 0x13, 0x7B, 0x51, 0x3A, 0x50, 0xE0, 0x03, 0x6E, 0xB7,
- 0x17, 0xEE, 0x58, 0xCE, 0xF8, 0x15, 0x3C, 0x70, 0xDE, 0xE6, 0xEB, 0xE6, 0xD4, 0x2C, 0x27, 0xB9,
- 0xCA, 0x15, 0xCE, 0x2E, 0x31, 0x54, 0xF5, 0x0A, 0x98, 0x8D, 0x78, 0xE5, 0xB6, 0xF8, 0xE4, 0x62,
- 0x43, 0xAA, 0x37, 0x93, 0xFF, 0xE3, 0xAB, 0x17, 0xC5, 0x81, 0x4F, 0xFD, 0xF1, 0x84, 0xE1, 0x8A,
- 0x99, 0xB0, 0x1D, 0x85, 0x80, 0xA2, 0x49, 0x35, 0x8D, 0xDD, 0xBC, 0x74, 0x0B, 0xBA, 0x33, 0x5B,
- 0xD5, 0x7A, 0xB9, 0x2F, 0x9B, 0x24, 0xA5, 0xAB, 0xF6, 0x1E, 0xE3, 0xA3, 0xA8, 0x0D, 0x1E, 0x48,
- 0xF7, 0xDB, 0xE2, 0x54, 0x65, 0x43, 0xA6, 0xD3, 0x3F, 0x2C, 0x9B, 0x13, 0x9A, 0xBE, 0x0F, 0x4D,
- 0x51, 0xC3, 0x73, 0xA5, 0xFE, 0xFC, 0x93, 0x12, 0xEF, 0x9C, 0x4D, 0x68, 0xE3, 0xDA, 0x52, 0x67,
- 0x28, 0x41, 0x17, 0x22, 0x3E, 0x33, 0xB0, 0x3A, 0xFB, 0x44, 0xB0, 0x2E, 0xA6, 0xD2, 0x95, 0xC0,
- 0x9A, 0xBA, 0x32, 0xA3, 0xC5, 0xFE, 0x86, 0x5D, 0xC8, 0xBB, 0xB5, 0xDE, 0x92, 0x8C, 0x7D, 0xE4,
- 0x03, 0xD4, 0xF9, 0xAE, 0x41, 0xE3, 0xBD, 0x35, 0x4B, 0x94, 0x27, 0xE0, 0x12, 0x21, 0x46, 0xE9,
- 0x09,
-};
-
-// This section defines and assigns function pointers to the libspotify functions
-// The arguments and return values must be in sync with the spotify api
-// Please scroll through the ugliness which follows
-
-typedef const char* (*fptr_sp_error_message_t)(sp_error error);
-
-typedef sp_error (*fptr_sp_session_create_t)(const sp_session_config *config, sp_session **sess);
-typedef sp_error (*fptr_sp_session_release_t)(sp_session *sess);
-typedef sp_error (*fptr_sp_session_login_t)(sp_session *session, const char *username, const char *password, bool remember_me, const char *blob);
-typedef sp_error (*fptr_sp_session_relogin_t)(sp_session *session);
-typedef sp_error (*fptr_sp_session_logout_t)(sp_session *session);
-typedef sp_error (*fptr_sp_session_process_events_t)(sp_session *session, int *next_timeout);
-typedef sp_playlist* (*fptr_sp_session_starred_create_t)(sp_session *session);
-typedef sp_playlistcontainer* (*fptr_sp_session_playlistcontainer_t)(sp_session *session);
-typedef sp_error (*fptr_sp_session_player_load_t)(sp_session *session, sp_track *track);
-typedef sp_error (*fptr_sp_session_player_unload_t)(sp_session *session);
-typedef sp_error (*fptr_sp_session_player_play_t)(sp_session *session, bool play);
-typedef sp_error (*fptr_sp_session_player_seek_t)(sp_session *session, int offset);
-typedef sp_connectionstate (*fptr_sp_session_connectionstate_t)(sp_session *session);
-typedef sp_error (*fptr_sp_session_preferred_bitrate_t)(sp_session *session, sp_bitrate bitrate);
-typedef const char* (*fptr_sp_session_user_name_t)(sp_session *session);
-
-typedef sp_error (*fptr_sp_playlistcontainer_add_callbacks_t)(sp_playlistcontainer *pc, sp_playlistcontainer_callbacks *callbacks, void *userdata);
-typedef int (*fptr_sp_playlistcontainer_num_playlists_t)(sp_playlistcontainer *pc);
-typedef sp_playlist* (*fptr_sp_playlistcontainer_playlist_t)(sp_playlistcontainer *pc, int index);
-
-typedef sp_error (*fptr_sp_playlist_add_callbacks_t)(sp_playlist *playlist, sp_playlist_callbacks *callbacks, void *userdata);
-typedef const char* (*fptr_sp_playlist_name_t)(sp_playlist *playlist);
-typedef sp_error (*fptr_sp_playlist_remove_callbacks_t)(sp_playlist *playlist, sp_playlist_callbacks *callbacks, void *userdata);
-typedef int (*fptr_sp_playlist_num_tracks_t)(sp_playlist *playlist);
-typedef sp_track* (*fptr_sp_playlist_track_t)(sp_playlist *playlist, int index);
-typedef bool (*fptr_sp_playlist_is_loaded_t)(sp_playlist *playlist);
-typedef int (*fptr_sp_playlist_track_create_time_t)(sp_playlist *playlist, int index);
-typedef sp_user* (*fptr_sp_playlist_owner_t)(sp_playlist *playlist);
-
-typedef sp_error (*fptr_sp_track_error_t)(sp_track *track);
-typedef bool (*fptr_sp_track_is_loaded_t)(sp_track *track);
-typedef const char* (*fptr_sp_track_name_t)(sp_track *track);
-typedef int (*fptr_sp_track_duration_t)(sp_track *track);
-typedef int (*fptr_sp_track_index_t)(sp_track *track);
-typedef int (*fptr_sp_track_disc_t)(sp_track *track);
-typedef sp_album* (*fptr_sp_track_album_t)(sp_track *track);
-typedef sp_track_availability (*fptr_sp_track_get_availability_t)(sp_session *session, sp_track *track);
-typedef bool (*fptr_sp_track_is_starred_t)(sp_session *session, sp_track *track);
-
-typedef sp_link* (*fptr_sp_link_create_from_playlist_t)(sp_playlist *playlist);
-typedef sp_link* (*fptr_sp_link_create_from_track_t)(sp_track *track, int offset);
-typedef sp_link* (*fptr_sp_link_create_from_string_t)(const char *link);
-typedef int (*fptr_sp_link_as_string_t)(sp_link *link, char *buffer, int buffer_size);
-typedef sp_track* (*fptr_sp_link_as_track_t)(sp_link *link);
-typedef sp_error (*fptr_sp_link_release_t)(sp_link *link);
-
-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);
-typedef sp_error (*fptr_sp_image_add_load_callback_t)(sp_image *image, image_loaded_cb *callback, void *userdata);
-typedef sp_error (*fptr_sp_image_remove_load_callback_t)(sp_image *image, image_loaded_cb *callback, void *userdata);
-
-typedef const char* (*fptr_sp_user_display_name_t)(sp_user *user);
-typedef const char* (*fptr_sp_user_canonical_name_t)(sp_user *user);
-
-/* Define actual function pointers */
-fptr_sp_error_message_t fptr_sp_error_message;
-
-fptr_sp_session_create_t fptr_sp_session_create;
-fptr_sp_session_release_t fptr_sp_session_release;
-fptr_sp_session_login_t fptr_sp_session_login;
-fptr_sp_session_relogin_t fptr_sp_session_relogin;
-fptr_sp_session_logout_t fptr_sp_session_logout;
-fptr_sp_session_starred_create_t fptr_sp_session_starred_create;
-fptr_sp_session_playlistcontainer_t fptr_sp_session_playlistcontainer;
-fptr_sp_session_process_events_t fptr_sp_session_process_events;
-fptr_sp_session_player_load_t fptr_sp_session_player_load;
-fptr_sp_session_player_unload_t fptr_sp_session_player_unload;
-fptr_sp_session_player_play_t fptr_sp_session_player_play;
-fptr_sp_session_player_seek_t fptr_sp_session_player_seek;
-fptr_sp_session_connectionstate_t fptr_sp_session_connectionstate;
-fptr_sp_session_preferred_bitrate_t fptr_sp_session_preferred_bitrate;
-fptr_sp_session_user_name_t fptr_sp_session_user_name;
-
-fptr_sp_playlistcontainer_add_callbacks_t fptr_sp_playlistcontainer_add_callbacks;
-fptr_sp_playlistcontainer_num_playlists_t fptr_sp_playlistcontainer_num_playlists;
-fptr_sp_playlistcontainer_playlist_t fptr_sp_playlistcontainer_playlist;
-
-fptr_sp_playlist_add_callbacks_t fptr_sp_playlist_add_callbacks;
-fptr_sp_playlist_name_t fptr_sp_playlist_name;
-fptr_sp_playlist_remove_callbacks_t fptr_sp_playlist_remove_callbacks;
-fptr_sp_playlist_num_tracks_t fptr_sp_playlist_num_tracks;
-fptr_sp_playlist_track_t fptr_sp_playlist_track;
-fptr_sp_playlist_is_loaded_t fptr_sp_playlist_is_loaded;
-fptr_sp_playlist_track_create_time_t fptr_sp_playlist_track_create_time;
-fptr_sp_playlist_owner_t fptr_sp_playlist_owner;
-
-fptr_sp_track_error_t fptr_sp_track_error;
-fptr_sp_track_is_loaded_t fptr_sp_track_is_loaded;
-fptr_sp_track_name_t fptr_sp_track_name;
-fptr_sp_track_duration_t fptr_sp_track_duration;
-fptr_sp_track_index_t fptr_sp_track_index;
-fptr_sp_track_disc_t fptr_sp_track_disc;
-fptr_sp_track_album_t fptr_sp_track_album;
-fptr_sp_track_get_availability_t fptr_sp_track_get_availability;
-fptr_sp_track_is_starred_t fptr_sp_track_is_starred;
-
-fptr_sp_link_create_from_playlist_t fptr_sp_link_create_from_playlist;
-fptr_sp_link_create_from_track_t fptr_sp_link_create_from_track;
-fptr_sp_link_create_from_string_t fptr_sp_link_create_from_string;
-fptr_sp_link_as_string_t fptr_sp_link_as_string;
-fptr_sp_link_as_track_t fptr_sp_link_as_track;
-fptr_sp_link_release_t fptr_sp_link_release;
-
-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;
-fptr_sp_image_add_load_callback_t fptr_sp_image_add_load_callback;
-fptr_sp_image_remove_load_callback_t fptr_sp_image_remove_load_callback;
-
-fptr_sp_user_display_name_t fptr_sp_user_display_name;
-fptr_sp_user_canonical_name_t fptr_sp_user_canonical_name;
-
-/* Assign function pointers to libspotify symbol */
-static int
-fptr_assign_all()
-{
- void *h;
- char *err;
- int ret;
-
- h = g_libhandle;
-
- // The following is non-ISO compliant
- ret = (fptr_sp_error_message = dlsym(h, "sp_error_message"))
- && (fptr_sp_session_create = dlsym(h, "sp_session_create"))
- && (fptr_sp_session_release = dlsym(h, "sp_session_release"))
- && (fptr_sp_session_login = dlsym(h, "sp_session_login"))
- && (fptr_sp_session_relogin = dlsym(h, "sp_session_relogin"))
- && (fptr_sp_session_logout = dlsym(h, "sp_session_logout"))
- && (fptr_sp_session_playlistcontainer = dlsym(h, "sp_session_playlistcontainer"))
- && (fptr_sp_session_process_events = dlsym(h, "sp_session_process_events"))
- && (fptr_sp_session_player_load = dlsym(h, "sp_session_player_load"))
- && (fptr_sp_session_player_unload = dlsym(h, "sp_session_player_unload"))
- && (fptr_sp_session_player_play = dlsym(h, "sp_session_player_play"))
- && (fptr_sp_session_player_seek = dlsym(h, "sp_session_player_seek"))
- && (fptr_sp_session_connectionstate = dlsym(h, "sp_session_connectionstate"))
- && (fptr_sp_session_preferred_bitrate = dlsym(h, "sp_session_preferred_bitrate"))
- && (fptr_sp_session_user_name = dlsym(h, "sp_session_user_name"))
- && (fptr_sp_playlistcontainer_add_callbacks = dlsym(h, "sp_playlistcontainer_add_callbacks"))
- && (fptr_sp_playlistcontainer_num_playlists = dlsym(h, "sp_playlistcontainer_num_playlists"))
- && (fptr_sp_session_starred_create = dlsym(h, "sp_session_starred_create"))
- && (fptr_sp_playlistcontainer_playlist = dlsym(h, "sp_playlistcontainer_playlist"))
- && (fptr_sp_playlist_add_callbacks = dlsym(h, "sp_playlist_add_callbacks"))
- && (fptr_sp_playlist_name = dlsym(h, "sp_playlist_name"))
- && (fptr_sp_playlist_remove_callbacks = dlsym(h, "sp_playlist_remove_callbacks"))
- && (fptr_sp_playlist_num_tracks = dlsym(h, "sp_playlist_num_tracks"))
- && (fptr_sp_playlist_track = dlsym(h, "sp_playlist_track"))
- && (fptr_sp_playlist_is_loaded = dlsym(h, "sp_playlist_is_loaded"))
- && (fptr_sp_playlist_track_create_time = dlsym(h, "sp_playlist_track_create_time"))
- && (fptr_sp_playlist_owner = dlsym(h, "sp_playlist_owner"))
- && (fptr_sp_track_error = dlsym(h, "sp_track_error"))
- && (fptr_sp_track_is_loaded = dlsym(h, "sp_track_is_loaded"))
- && (fptr_sp_track_name = dlsym(h, "sp_track_name"))
- && (fptr_sp_track_duration = dlsym(h, "sp_track_duration"))
- && (fptr_sp_track_index = dlsym(h, "sp_track_index"))
- && (fptr_sp_track_disc = dlsym(h, "sp_track_disc"))
- && (fptr_sp_track_album = dlsym(h, "sp_track_album"))
- && (fptr_sp_track_get_availability = dlsym(h, "sp_track_get_availability"))
- && (fptr_sp_track_is_starred = dlsym(h, "sp_track_is_starred"))
- && (fptr_sp_link_create_from_playlist = dlsym(h, "sp_link_create_from_playlist"))
- && (fptr_sp_link_create_from_track = dlsym(h, "sp_link_create_from_track"))
- && (fptr_sp_link_create_from_string = dlsym(h, "sp_link_create_from_string"))
- && (fptr_sp_link_as_string = dlsym(h, "sp_link_as_string"))
- && (fptr_sp_link_as_track = dlsym(h, "sp_link_as_track"))
- && (fptr_sp_link_release = dlsym(h, "sp_link_release"))
- && (fptr_sp_album_name = dlsym(h, "sp_album_name"))
- && (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"))
- && (fptr_sp_image_add_load_callback = dlsym(h, "sp_image_add_load_callback"))
- && (fptr_sp_image_remove_load_callback = dlsym(h, "sp_image_remove_load_callback"))
- && (fptr_sp_user_display_name = dlsym(h, "sp_user_display_name"))
- && (fptr_sp_user_canonical_name = dlsym(h, "sp_user_canonical_name"))
- ;
-
- err = dlerror();
-
- if (ret && !err)
- return ret;
- else if (err)
- DPRINTF(E_LOG, L_SPOTIFY, "Assignment error (%d): %s\n", ret, err);
- else
- DPRINTF(E_LOG, L_SPOTIFY, "Unknown assignment error (%d)\n", ret);
-
- return -1;
-}
-// End of ugly part
-
-
-/* -------------------------- PLAYLIST HELPERS ------------------------- */
-/* Should only be called from within the spotify thread */
-
-
-// Registers a track with libspotify, which will make it start loading the track
-// metadata. When that is done metadata_updated() is called (but we won't be
-// told which track it was...). Note that this function will result in a ref
-// count on the sp_link, which the caller must decrease with sp_link_release.
-static enum command_state
-uri_register(void *arg, int *retval)
-{
- sp_link *link;
- sp_track *track;
-
- char *uri = arg;
-
- if (SP_CONNECTION_STATE_LOGGED_IN != fptr_sp_session_connectionstate(g_sess))
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Can't register music, not connected and logged in to Spotify\n");
- *retval = -1;
- return COMMAND_END;
- }
-
- link = fptr_sp_link_create_from_string(uri);
- if (!link)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Invalid Spotify link: '%s'\n", uri);
- *retval = -1;
- return COMMAND_END;
- }
-
- track = fptr_sp_link_as_track(link);
- if (!track)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Invalid Spotify track: '%s'\n", uri);
- *retval = -1;
- return COMMAND_END;
- }
-
- *retval = 0;
- return COMMAND_END;
-}
-
-static void
-webapi_playlist_updated(sp_playlist *pl)
-{
- sp_link *link;
- char url[1024];
- int ret;
-
- // Run playlist save in the library thread
- link = fptr_sp_link_create_from_playlist(pl);
- if (!link)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not create link for playlist: '%s'\n", fptr_sp_playlist_name(pl));
- return;
- }
-
- ret = fptr_sp_link_as_string(link, url, sizeof(url));
- if (ret == sizeof(url))
- {
- DPRINTF(E_DBG, L_SPOTIFY, "Spotify link truncated: %s\n", url);
- }
- fptr_sp_link_release(link);
-
- spotifywebapi_pl_save(url);
-}
-
-/* -------------------------- PLAYLIST CALLBACKS ------------------------- */
-/**
- * Called when a playlist is updating or is done updating
- *
- * This is called before and after a series of changes are applied to the
- * playlist. It allows e.g. the user interface to defer updating until the
- * entire operation is complete.
- *
- * @param[in] pl Playlist object
- * @param[in] done True iff the update is completed
- * @param[in] userdata Userdata passed to sp_playlist_add_callbacks()
- */
-static void playlist_update_in_progress(sp_playlist *pl, bool done, void *userdata)
-{
- if (done)
- {
- DPRINTF(E_DBG, L_SPOTIFY, "Playlist update (status %d): %s\n", done, fptr_sp_playlist_name(pl));
-
- webapi_playlist_updated(pl);
- }
-}
-
-static void playlist_metadata_updated(sp_playlist *pl, void *userdata)
-{
- DPRINTF(E_DBG, L_SPOTIFY, "Playlist metadata updated: %s\n", fptr_sp_playlist_name(pl));
-
- //TODO Update disabled to prevent multiple triggering of updates e. g. on adding a playlist
-}
-
-/**
- * The callbacks we are interested in for individual playlists.
- */
-static sp_playlist_callbacks pl_callbacks = {
- .playlist_update_in_progress = &playlist_update_in_progress,
- .playlist_metadata_updated = &playlist_metadata_updated,
-};
-
-
-/* -------------------- PLAYLIST CONTAINER CALLBACKS --------------------- */
-/**
- * Callback from libspotify, telling us a playlist was added to the playlist container.
- *
- * We add our playlist callbacks to the newly added playlist.
- *
- * @param pc The playlist container handle
- * @param pl The playlist handle
- * @param position Index of the added playlist
- * @param userdata The opaque pointer
- */
-static void playlist_added(sp_playlistcontainer *pc, sp_playlist *pl,
- int position, void *userdata)
-{
- DPRINTF(E_INFO, L_SPOTIFY, "Playlist added: %s (%d tracks)\n", fptr_sp_playlist_name(pl), fptr_sp_playlist_num_tracks(pl));
-
- fptr_sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
-
- webapi_playlist_updated(pl);
-}
-
-/**
- * Callback from libspotify, telling us a playlist was removed from the playlist container.
- *
- * This is the place to remove our playlist callbacks.
- *
- * @param pc The playlist container handle
- * @param pl The playlist handle
- * @param position Index of the removed playlist
- * @param userdata The opaque pointer
- */
-static void
-playlist_removed(sp_playlistcontainer *pc, sp_playlist *pl, int position, void *userdata)
-{
- sp_link *link;
- char url[1024];
- int ret;
-
- DPRINTF(E_INFO, L_SPOTIFY, "Playlist removed: %s\n", fptr_sp_playlist_name(pl));
-
- fptr_sp_playlist_remove_callbacks(pl, &pl_callbacks, NULL);
-
- link = fptr_sp_link_create_from_playlist(pl);
- if (!link)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not find link for deleted playlist\n");
- return;
- }
-
- ret = fptr_sp_link_as_string(link, url, sizeof(url));
- if (ret == sizeof(url))
- {
- DPRINTF(E_DBG, L_SPOTIFY, "Spotify link truncated: %s\n", url);
- }
- fptr_sp_link_release(link);
-
- // Run playlist remove in the library thread
- spotifywebapi_pl_remove(url);
-}
-
-/**
- * Callback from libspotify, telling us the rootlist is fully synchronized
- *
- * @param pc The playlist container handle
- * @param userdata The opaque pointer
- */
-static void
-container_loaded(sp_playlistcontainer *pc, void *userdata)
-{
- int num;
-
- num = fptr_sp_playlistcontainer_num_playlists(pc);
-
- DPRINTF(E_INFO, L_SPOTIFY, "Rootlist synchronized (%d playlists)\n", num);
-}
-
-
-/**
- * The playlist container callbacks
- */
-static sp_playlistcontainer_callbacks pc_callbacks = {
- .playlist_added = &playlist_added,
- .playlist_removed = &playlist_removed,
- .container_loaded = &container_loaded,
-};
-
-
-/* --------------------- INTERNAL PLAYBACK AND AUDIO ----------------------- */
-/* Should only be called from within the spotify thread */
-
-static enum command_state
-playback_setup(void *arg, int *retval)
-{
- sp_link *link;
- sp_track *track;
- sp_error err;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Setting up for playback\n");
-
- link = (sp_link *) arg;
-
- if (SP_CONNECTION_STATE_LOGGED_IN != fptr_sp_session_connectionstate(g_sess))
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Can't play music, not connected and logged in to Spotify\n");
- *retval = -1;
- return COMMAND_END;
- }
-
- if (!link)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Playback setup failed, no Spotify link\n");
- *retval = -1;
- return COMMAND_END;
- }
-
- track = fptr_sp_link_as_track(link);
- if (!track)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Playback setup failed, invalid Spotify track\n");
- *retval = -1;
- return COMMAND_END;
- }
-
- err = fptr_sp_session_player_load(g_sess, track);
- if (SP_ERROR_OK != err)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Playback setup failed: %s\n", fptr_sp_error_message(err));
- *retval = (SP_ERROR_IS_LOADING == err) ? LIBSPOTIFY_SETUP_ERROR_IS_LOADING : -1;
- return COMMAND_END;
- }
-
- *retval = 0;
- return COMMAND_END;
-}
-
-static enum command_state
-playback_play(void *arg, int *retval)
-{
- sp_error err;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Starting playback\n");
-
- err = fptr_sp_session_player_play(g_sess, 1);
- if (SP_ERROR_OK != err)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Playback failed: %s\n", fptr_sp_error_message(err));
- *retval = -1;
- return COMMAND_END;
- }
-
- g_state = SPOTIFY_STATE_PLAYING;
-
- *retval = 0;
- return COMMAND_END;
-}
-
-static enum command_state
-playback_pause(void *arg, int *retval)
-{
- sp_error err;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Pausing playback\n");
-
- err = fptr_sp_session_player_play(g_sess, 0);
- DPRINTF(E_DBG, L_SPOTIFY, "Playback paused\n");
-
- if (SP_ERROR_OK != err)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Playback pause failed: %s\n", fptr_sp_error_message(err));
- *retval = -1;
- return COMMAND_END;
- }
-
- g_state = SPOTIFY_STATE_PAUSED;
-
- *retval = 0;
- return COMMAND_END;
-}
-
-static enum command_state
-playback_stop(void *arg, int *retval)
-{
- sp_error err;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Stopping playback\n");
-
- err = fptr_sp_session_player_unload(g_sess);
- if (SP_ERROR_OK != err)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Playback stop failed: %s\n", fptr_sp_error_message(err));
- *retval = -1;
- return COMMAND_END;
- }
-
- g_state = SPOTIFY_STATE_STOPPED;
-
- evbuffer_drain(spotify_audio_buffer, evbuffer_get_length(spotify_audio_buffer));
-
- *retval = 0;
- return COMMAND_END;
-}
-
-static enum command_state
-playback_seek(void *arg, int *retval)
-{
- int seek_ms;
- sp_error err;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Playback seek\n");
-
- seek_ms = *((int *) arg);
-
- err = fptr_sp_session_player_seek(g_sess, seek_ms);
- if (SP_ERROR_OK != err)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not seek: %s\n", fptr_sp_error_message(err));
- *retval = -1;
- return COMMAND_END;
- }
-
- *retval = 0;
- return COMMAND_END;
-}
-
-static enum command_state
-playback_eot(void *arg, int *retval)
-{
- sp_error err;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Playback end of track\n");
-
- err = fptr_sp_session_player_unload(g_sess);
- if (SP_ERROR_OK != err)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Playback end of track failed: %s\n", fptr_sp_error_message(err));
- *retval = -1;
- return COMMAND_END;
- }
-
- g_state = SPOTIFY_STATE_STOPPING;
-
- input_write(spotify_audio_buffer, NULL, INPUT_FLAG_EOF);
-
- *retval = 0;
- 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.
- *
- * @sa sp_session_callbacks#logged_in
- */
-static void
-logged_in(sp_session *sess, sp_error error)
-{
- sp_playlist *pl;
- sp_playlistcontainer *pc;
- int i;
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&login_lck));
-
- if (SP_ERROR_OK != error)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Login failed: %s\n", fptr_sp_error_message(error));
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&status_lck));
- spotify_status_info.libspotify_logged_in = false;
- spotify_status_info.libspotify_user[0] = '\0';
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&status_lck));
-
- CHECK_ERR(L_SPOTIFY, pthread_cond_signal(&login_cond));
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
-
- listener_notify(LISTENER_SPOTIFY);
-
- return;
- }
-
- DPRINTF(E_LOG, L_SPOTIFY, "Login to Spotify succeeded (libspotify)\n");
-
- pl = fptr_sp_session_starred_create(sess);
- fptr_sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
-
- pc = fptr_sp_session_playlistcontainer(sess);
-
- fptr_sp_playlistcontainer_add_callbacks(pc, &pc_callbacks, NULL);
-
- DPRINTF(E_DBG, L_SPOTIFY, "Found %d playlists\n", fptr_sp_playlistcontainer_num_playlists(pc));
-
- for (i = 0; i < fptr_sp_playlistcontainer_num_playlists(pc); i++)
- {
- pl = fptr_sp_playlistcontainer_playlist(pc, i);
- fptr_sp_playlist_add_callbacks(pl, &pl_callbacks, NULL);
- }
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&status_lck));
- spotify_status_info.libspotify_logged_in = true;
- snprintf(spotify_status_info.libspotify_user, sizeof(spotify_status_info.libspotify_user), "%s", fptr_sp_session_user_name(sess));
- spotify_status_info.libspotify_user[sizeof(spotify_status_info.libspotify_user) - 1] = '\0';
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&status_lck));
-
- CHECK_ERR(L_SPOTIFY, pthread_cond_signal(&login_cond));
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
-
- listener_notify(LISTENER_SPOTIFY);
-}
-
-/**
- * Called when logout has been processed.
- * Either called explicitly if you initialize a logout operation, or implicitly
- * if there is a permanent connection error
- *
- * @sa sp_session_callbacks#logged_out
- */
-static void
-logged_out(sp_session *sess)
-{
- DPRINTF(E_INFO, L_SPOTIFY, "Logout complete\n");
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&status_lck));
- spotify_status_info.libspotify_logged_in = false;
- spotify_status_info.libspotify_user[0] = '\0';
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&status_lck));
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&login_lck));
-
- CHECK_ERR(L_SPOTIFY, pthread_cond_signal(&login_cond));
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
-
- listener_notify(LISTENER_SPOTIFY);
-}
-
-/**
- * This callback is used from libspotify whenever there is PCM data available.
- *
- * @sa sp_session_callbacks#music_delivery
- */
-static int music_delivery(sp_session *sess, const sp_audioformat *format,
- const void *frames, int num_frames)
-{
- struct media_quality quality = { 0 };
- size_t size;
- int ret;
-
- /* No support for resampling right now */
- if ((format->sample_type != SP_SAMPLETYPE_INT16_NATIVE_ENDIAN) || (format->channels != 2))
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Got music with unsupported sample format or number of channels, stopping playback\n");
- libspotify_playback_stop_nonblock();
- return num_frames;
- }
-
- quality.sample_rate = format->sample_rate;
- quality.bits_per_sample = 16;
- quality.channels = format->channels;
-
- // Audio discontinuity, e.g. seek
- if (num_frames == 0)
- {
- evbuffer_drain(spotify_audio_buffer, evbuffer_get_length(spotify_audio_buffer));
- return 0;
- }
-
- size = num_frames * sizeof(int16_t) * format->channels;
-
- ret = evbuffer_add(spotify_audio_buffer, frames, size);
- if (ret < 0)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Out of memory adding audio to buffer\n");
- return num_frames;
- }
-
- // The input buffer only accepts writing when it is approaching depletion, and
- // because we use NONBLOCK it will just return if this is not the case. So in
- // most cases no actual write is made and spotify_audio_buffer will just grow.
- input_write(spotify_audio_buffer, &quality, 0);
-
- return num_frames;
-}
-
-/**
- * This callback is called from an internal libspotify thread to ask us to
- * reiterate the main loop. This must not block.
- *
- * @sa sp_session_callbacks#notify_main_thread
- */
-static void
-notify_main_thread(sp_session *sess)
-{
- int dummy = 42;
- int ret;
-
- ret = write(g_notify_pipe[1], &dummy, sizeof(dummy));
- if (ret != sizeof(dummy))
- DPRINTF(E_LOG, L_SPOTIFY, "Could not write to notify fd: %s\n", strerror(errno));
-}
-
-/**
- * Called whenever metadata has been updated
- *
- * If you have metadata cached outside of libspotify, you should purge
- * your caches and fetch new versions.
- *
- * @param[in] session Session
- */
-static void metadata_updated(sp_session *session)
-{
- DPRINTF(E_DBG, L_SPOTIFY, "Session metadata updated\n");
-}
-
-/* Misc connection error callbacks */
-static void play_token_lost(sp_session *sess)
-{
- DPRINTF(E_LOG, L_SPOTIFY, "Music interrupted - some other session is playing on the account\n");
-
- libspotify_playback_stop_nonblock();
-}
-
-static void connectionstate_updated(sp_session *session)
-{
- if (SP_CONNECTION_STATE_LOGGED_IN == fptr_sp_session_connectionstate(session))
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Connection to Spotify (re)established (libspotify)\n");
- }
- else if (g_state == SPOTIFY_STATE_PLAYING)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Music interrupted - connection error or logged out\n");
- libspotify_playback_stop_nonblock();
- }
-}
-
-/**
- * This callback is used from libspotify when the current track has ended
- *
- * @sa sp_session_callbacks#end_of_track
- */
-static void end_of_track(sp_session *sess)
-{
- DPRINTF(E_DBG, L_SPOTIFY, "End of track\n");
-
- commands_exec_async(cmdbase, playback_eot, NULL);
-}
-
-/**
- * The session callbacks
- */
-static sp_session_callbacks session_callbacks = {
- .logged_in = &logged_in,
- .logged_out = &logged_out,
- .connectionstate_updated = &connectionstate_updated,
- .notify_main_thread = ¬ify_main_thread,
- .music_delivery = &music_delivery,
- .metadata_updated = &metadata_updated,
- .play_token_lost = &play_token_lost,
- .log_message = NULL,
- .end_of_track = &end_of_track,
-};
-
-/**
- * The session configuration.
- */
-static sp_session_config spconfig = {
- .api_version = SPOTIFY_API_VERSION,
- .cache_location = NULL,
- .settings_location = NULL,
- .application_key = g_appkey,
- .application_key_size = sizeof(g_appkey),
- .user_agent = "forked-daapd",
- .callbacks = &session_callbacks,
- NULL,
-};
-
-
-/* ------------------------------- MAIN LOOP ------------------------------- */
-/* Thread: spotify */
-
-static void *
-spotify(void *arg)
-{
- int ret;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Main loop initiating\n");
-
- ret = db_perthread_init();
- if (ret < 0)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Error: DB init failed\n");
- pthread_exit(NULL);
- }
-
- g_state = SPOTIFY_STATE_WAIT;
-
- event_base_dispatch(evbase_spotify);
-
- if (g_state != SPOTIFY_STATE_INACTIVE)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Spotify event loop terminated ahead of time!\n");
- g_state = SPOTIFY_STATE_INACTIVE;
- }
-
- db_perthread_deinit();
-
- DPRINTF(E_DBG, L_SPOTIFY, "Main loop terminating\n");
-
- pthread_exit(NULL);
-}
-
-static void
-exit_cb()
-{
- fptr_sp_session_player_unload(g_sess);
- fptr_sp_session_logout(g_sess);
- g_state = SPOTIFY_STATE_INACTIVE;
-}
-
-/* Process events when timeout expires or triggered by libspotify's notify_main_thread */
-static void
-notify_cb(int fd, short what, void *arg)
-{
- struct timeval tv;
- int next_timeout;
- int dummy;
- int ret;
-
- if (what & EV_READ)
- {
- ret = read(g_notify_pipe[0], &dummy, sizeof(dummy));
- if (ret != sizeof(dummy))
- DPRINTF(E_LOG, L_SPOTIFY, "Error reading from notify pipe\n");
- }
-
- do
- {
- fptr_sp_session_process_events(g_sess, &next_timeout);
- }
- while (next_timeout == 0);
-
- tv.tv_sec = next_timeout / 1000;
- tv.tv_usec = (next_timeout % 1000) * 1000;
-
- event_add(g_notifyev, &tv);
-}
-
-
-/* ---------------------------- Our Spotify API --------------------------- */
-
-/* Thread: player */
-int
-libspotify_playback_setup(const char *path)
-{
- sp_link *link;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Playback setup request\n");
-
- link = fptr_sp_link_create_from_string(path);
- if (!link)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Playback setup failed, invalid Spotify link: %s\n", path);
- return -1;
- }
-
- return commands_exec_sync(cmdbase, playback_setup, NULL, link);
-}
-
-int
-libspotify_playback_play()
-{
- DPRINTF(E_DBG, L_SPOTIFY, "Playback request\n");
-
- return commands_exec_sync(cmdbase, playback_play, NULL, NULL);
-}
-
-int
-libspotify_playback_pause()
-{
- DPRINTF(E_DBG, L_SPOTIFY, "Pause request\n");
-
- return commands_exec_sync(cmdbase, playback_pause, NULL, NULL);
-}
-
-/* Thread: libspotify */
-void
-libspotify_playback_pause_nonblock(void)
-{
- DPRINTF(E_DBG, L_SPOTIFY, "Nonblock pause request\n");
-
- commands_exec_async(cmdbase, playback_pause, NULL);
-}
-
-/* Thread: player and libspotify */
-int
-libspotify_playback_stop(void)
-{
- DPRINTF(E_DBG, L_SPOTIFY, "Stop request\n");
-
- return commands_exec_sync(cmdbase, playback_stop, NULL, NULL);
-}
-
-/* Thread: player and libspotify */
-void
-libspotify_playback_stop_nonblock(void)
-{
- DPRINTF(E_DBG, L_SPOTIFY, "Nonblock stop request\n");
-
- commands_exec_async(cmdbase, playback_stop, NULL);
-}
-
-/* Thread: player */
-int
-libspotify_playback_seek(int ms)
-{
- int ret;
-
- ret = commands_exec_sync(cmdbase, playback_seek, NULL, &ms);
-
- if (ret == 0)
- return ms;
- else
- return -1;
-}
-
-/* Thread: httpd (artwork) and worker */
-int
-libspotify_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
-libspotify_uri_register(const char *uri)
-{
- char *tmp;
-
- tmp = strdup(uri);
- commands_exec_async(cmdbase, uri_register, tmp);
-}
-
-void
-libspotify_status_info_get(struct spotify_status_info *info)
-{
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&status_lck));
- memcpy(info, &spotify_status_info, sizeof(struct spotify_status_info));
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&status_lck));
-}
-
-/* Thread: library, httpd */
-static int
-logout(const char **errmsg)
-{
- sp_error err;
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&login_lck));
-
- if (SP_CONNECTION_STATE_LOGGED_IN != fptr_sp_session_connectionstate(g_sess))
- {
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
- return 0;
- }
-
- DPRINTF(E_LOG, L_SPOTIFY, "Logging out of Spotify (current state is %d)\n", g_state);
-
- fptr_sp_session_player_unload(g_sess);
- err = fptr_sp_session_logout(g_sess);
- if (SP_ERROR_OK != err)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not logout of Spotify: %s\n", fptr_sp_error_message(err));
- if (errmsg)
- *errmsg = fptr_sp_error_message(err);
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
- return -1;
- }
-
- CHECK_ERR(L_SPOTIFY, pthread_cond_wait(&login_cond, &login_lck)); // Wait for logged_out()
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
-
- return 0;
-}
-
-/* Thread: library, httpd */
-static int
-login_user(const char *user, const char *password, const char **errmsg)
-{
- sp_error err;
- int ret;
-
- if (!g_sess)
- {
- if (!g_libhandle)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Can't login! - could not find libspotify\n");
- if (errmsg)
- *errmsg = "Could not find libspotify";
- }
- else
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Can't login! - no valid Spotify session\n");
- if (errmsg)
- *errmsg = "No valid Spotify session";
- }
-
- return -1;
- }
-
- ret = logout(errmsg);
- if (ret < 0)
- return -1;
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&login_lck));
-
- if (user && password)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Spotify credentials, logging in with username '%s' (libspotify)\n", user);
- err = fptr_sp_session_login(g_sess, user, password, 1, NULL);
- }
- else
- {
- DPRINTF(E_INFO, L_SPOTIFY, "Relogin to Spotify\n");
- err = fptr_sp_session_relogin(g_sess);
- }
-
- if (SP_ERROR_OK != err)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not login into Spotify: %s\n", fptr_sp_error_message(err));
- if (errmsg)
- *errmsg = fptr_sp_error_message(err);
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
- return -1;
- }
-
- CHECK_ERR(L_SPOTIFY, pthread_cond_wait(&login_cond, &login_lck));
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&login_lck));
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&status_lck));
- ret = spotify_status_info.libspotify_logged_in ? 0 : -1;
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&status_lck));
-
- if (ret < 0 && errmsg)
- *errmsg = "Login failed";
-
- return ret;
-}
-
-/* Thread: httpd, library */
-int
-libspotify_login(const char *user, const char *password, const char **errmsg)
-{
- int ret;
-
- ret = login_user(user, password, errmsg);
- if (ret == 0)
- {
- // Trigger scan after successful login to libspotify
- spotifywebapi_rescan();
- }
-
- return ret;
-}
-
-/* Thread: library */
-int
-libspotify_relogin(void)
-{
- return login_user(NULL, NULL, NULL);
-}
-
-void
-libspotify_logout(void)
-{
- logout(NULL);
-}
-
-/* Thread: main */
-int
-libspotify_init(void)
-{
- cfg_t *spotify_cfg;
- sp_session *sp;
- sp_error err;
- int ret;
-
- /* Initialize libspotify */
- g_libhandle = dlopen("libspotify.so", RTLD_LAZY);
- if (!g_libhandle)
- {
- DPRINTF(E_INFO, L_SPOTIFY, "libspotify.so not installed or not found\n");
- goto libspotify_fail;
- }
-
- ret = fptr_assign_all();
- if (ret < 0)
- goto assign_fail;
-
-#ifdef HAVE_PIPE2
- ret = pipe2(g_notify_pipe, O_CLOEXEC);
-#else
- ret = pipe(g_notify_pipe);
-#endif
- if (ret < 0)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not notify command pipe: %s\n", strerror(errno));
- goto notify_fail;
- }
-
- evbase_spotify = event_base_new();
- if (!evbase_spotify)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not create an event base\n");
- goto evbase_fail;
- }
-
- g_notifyev = event_new(evbase_spotify, g_notify_pipe[0], EV_READ | EV_TIMEOUT, notify_cb, NULL);
- if (!g_notifyev)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not create notify event\n");
- goto evnew_fail;
- }
-
- event_add(g_notifyev, NULL);
-
- cmdbase = commands_base_new(evbase_spotify, exit_cb);
- if (!cmdbase)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not create command base\n");
- goto cmd_fail;
- }
-
- DPRINTF(E_INFO, L_SPOTIFY, "Spotify session init\n");
-
- spotify_cfg = cfg_getsec(cfg, "spotify");
- spconfig.settings_location = cfg_getstr(spotify_cfg, "settings_dir");
- spconfig.cache_location = cfg_getstr(spotify_cfg, "cache_dir");
-
- DPRINTF(E_DBG, L_SPOTIFY, "Creating Spotify session\n");
- err = fptr_sp_session_create(&spconfig, &sp);
- if (SP_ERROR_OK != err)
- {
- DPRINTF(E_LOG, L_SPOTIFY, "Could not create Spotify session: %s\n", fptr_sp_error_message(err));
- goto session_fail;
- }
-
- g_sess = sp;
- g_state = SPOTIFY_STATE_INACTIVE;
-
- switch (cfg_getint(spotify_cfg, "bitrate"))
- {
- case 1:
- fptr_sp_session_preferred_bitrate(g_sess, SP_BITRATE_96k);
- break;
- case 2:
- fptr_sp_session_preferred_bitrate(g_sess, SP_BITRATE_160k);
- break;
- case 3:
- fptr_sp_session_preferred_bitrate(g_sess, SP_BITRATE_320k);
- break;
- }
-
- CHECK_ERR(L_SPOTIFY, pthread_mutex_lock(&status_lck));
- spotify_status_info.libspotify_installed = true;
- CHECK_ERR(L_SPOTIFY, pthread_mutex_unlock(&status_lck));
-
- spotify_audio_buffer = evbuffer_new();
-
- CHECK_ERR(L_SPOTIFY, evbuffer_enable_locking(spotify_audio_buffer, NULL));
-
- CHECK_ERR(L_SPOTIFY, mutex_init(&login_lck));
- CHECK_ERR(L_SPOTIFY, pthread_cond_init(&login_cond, NULL));
-
- /* Spawn thread */
- ret = pthread_create(&tid_spotify, NULL, spotify, NULL);
- if (ret < 0)
- {
- DPRINTF(E_FATAL, L_SPOTIFY, "Could not spawn Spotify thread: %s\n", strerror(errno));
- goto thread_fail;
- }
-
- thread_setname(tid_spotify, "spotify");
-
- DPRINTF(E_DBG, L_SPOTIFY, "Spotify init complete\n");
- return 0;
-
- thread_fail:
- 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));
-
- evbuffer_free(spotify_audio_buffer);
-
- fptr_sp_session_release(g_sess);
- g_sess = NULL;
-
- session_fail:
- cmd_fail:
- event_free(g_notifyev);
- evnew_fail:
- commands_base_free(cmdbase);
- event_base_free(evbase_spotify);
- evbase_spotify = NULL;
-
- evbase_fail:
- close(g_notify_pipe[0]);
- close(g_notify_pipe[1]);
-
- notify_fail:
- assign_fail:
- dlclose(g_libhandle);
- g_libhandle = NULL;
-
- libspotify_fail:
- return -1;
-}
-
-void
-libspotify_deinit(void)
-{
- int ret;
-
- if (!g_libhandle)
- return;
-
- /* Send exit signal to thread (if active) */
- if (g_state != SPOTIFY_STATE_INACTIVE)
- {
- commands_base_destroy(cmdbase);
- g_state = SPOTIFY_STATE_INACTIVE;
-
- ret = pthread_join(tid_spotify, NULL);
- if (ret != 0)
- {
- DPRINTF(E_FATAL, L_SPOTIFY, "Could not join Spotify thread: %s\n", strerror(errno));
- return;
- }
- }
-
- /* Release session */
- fptr_sp_session_release(g_sess);
-
- /* Free event base */
- event_free(g_notifyev);
- event_base_free(evbase_spotify);
-
- /* Close pipes */
- close(g_notify_pipe[0]);
- close(g_notify_pipe[1]);
-
- /* Destroy locks */
- 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));
-
- /* Free audio buffer */
- evbuffer_free(spotify_audio_buffer);
-
- /* Release libspotify handle */
- dlclose(g_libhandle);
-}
diff --git a/src/inputs/libspotify/libspotify.h b/src/inputs/libspotify/libspotify.h
deleted file mode 100644
index 1b1d3509..00000000
--- a/src/inputs/libspotify/libspotify.h
+++ /dev/null
@@ -1,65 +0,0 @@
-
-#ifndef __LIBSPOTIFY_H__
-#define __LIBSPOTIFY_H__
-
-#include
-#include
-#include
-#include
-
-
-struct spotify_status_info
-{
- bool libspotify_installed;
- bool libspotify_logged_in;
- char libspotify_user[100];
-};
-
-#define LIBSPOTIFY_SETUP_ERROR_IS_LOADING -2
-
-int
-libspotify_playback_setup(const char *path);
-
-int
-libspotify_playback_play(void);
-
-int
-libspotify_playback_pause(void);
-
-//void
-//spotify_playback_pause_nonblock(void);
-
-int
-libspotify_playback_stop(void);
-
-//void
-//spotify_playback_stop_nonblock(void);
-
-int
-libspotify_playback_seek(int ms);
-
-//int
-//spotify_artwork_get(struct evbuffer *evbuf, char *path, int max_w, int max_h);
-
-int
-libspotify_relogin(void);
-
-int
-libspotify_login(const char *user, const char *password, const char **errmsg);
-
-void
-libspotify_logout(void);
-
-void
-libspotify_status_info_get(struct spotify_status_info *info);
-
-void
-libspotify_uri_register(const char *uri);
-
-int
-libspotify_init(void);
-
-void
-libspotify_deinit(void);
-
-#endif /* !__LIBSPOTIFY_H__ */
diff --git a/src/inputs/spotify.c b/src/inputs/spotify.c
index eda1925e..57450370 100644
--- a/src/inputs/spotify.c
+++ b/src/inputs/spotify.c
@@ -25,26 +25,21 @@
#include
#include "logger.h"
-#include "conffile.h"
#include "spotify.h"
+// With just one backend the abstraction implemented here is a bit overkill, but
+// it was added back when there was also libspotify. Keep it around for a while
+// and then consider removing.
+
#ifdef SPOTIFY_LIBRESPOTC
extern struct spotify_backend spotify_librespotc;
#endif
-#ifdef SPOTIFY_LIBSPOTIFY
-extern struct spotify_backend spotify_libspotify;
-#endif
static struct spotify_backend *
backend_set(void)
{
#ifdef SPOTIFY_LIBRESPOTC
- if (!cfg_getbool(cfg_getsec(cfg, "spotify"), "use_libspotify"))
- return &spotify_librespotc;
-#endif
-#ifdef SPOTIFY_LIBSPOTIFY
- if (cfg_getbool(cfg_getsec(cfg, "spotify"), "use_libspotify"))
- return &spotify_libspotify;
+ return &spotify_librespotc;
#endif
DPRINTF(E_LOG, L_SPOTIFY, "Invalid Spotify configuration (not built with the configured backend)\n");
return NULL;
diff --git a/src/inputs/spotify_librespotc.c b/src/inputs/spotify_librespotc.c
index 6e560ba5..890fcd8a 100644
--- a/src/inputs/spotify_librespotc.c
+++ b/src/inputs/spotify_librespotc.c
@@ -286,7 +286,7 @@ hexdump_cb(const char *msg, uint8_t *data, size_t data_len)
}
-/* ----------------------- libresport-c initialization ---------------------- */
+/* ------------------------ librespot-c initialization ---------------------- */
struct sp_callbacks callbacks = {
.https_get = https_get_cb,
@@ -310,9 +310,6 @@ initialize(struct global_ctx *ctx)
spotify_cfg = cfg_getsec(cfg, "spotify");
- if (cfg_getbool(spotify_cfg, "use_libspotify"))
- return -1;
-
if (ctx->is_initialized)
return 0;
diff --git a/src/inputs/spotify_libspotify.c b/src/inputs/spotify_libspotify.c
deleted file mode 100644
index 28ce10b8..00000000
--- a/src/inputs/spotify_libspotify.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2017 Espen Jurgensen
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include
-#include
-#include
-#include
-
-#include "input.h"
-#include "conffile.h"
-#include "logger.h"
-#include "spotify.h"
-#include "libspotify/libspotify.h"
-
-// How many retries to start playback if resource is still loading
-#define LIBSPOTIFY_SETUP_RETRIES 5
-// How long to wait between retries in microseconds (500000 = 0.5 seconds)
-#define LIBSPOTIFY_SETUP_RETRY_WAIT 500000
-
-static int
-init(void)
-{
- return cfg_getbool(cfg_getsec(cfg, "spotify"), "use_libspotify") ? 0 : -1;
-}
-
-static int
-setup(struct input_source *source)
-{
- int i = 0;
- int ret;
-
- while((ret = libspotify_playback_setup(source->path)) == LIBSPOTIFY_SETUP_ERROR_IS_LOADING)
- {
- if (i >= LIBSPOTIFY_SETUP_RETRIES)
- break;
-
- DPRINTF(E_DBG, L_SPOTIFY, "Resource still loading (%d)\n", i);
- usleep(LIBSPOTIFY_SETUP_RETRY_WAIT);
- i++;
- }
-
- if (ret < 0)
- return -1;
-
- ret = libspotify_playback_play();
- if (ret < 0)
- return -1;
-
- return 0;
-}
-
-static int
-stop(struct input_source *source)
-{
- int ret;
-
- ret = libspotify_playback_stop();
- if (ret < 0)
- return -1;
-
- return 0;
-}
-
-static int
-seek(struct input_source *source, int seek_ms)
-{
- int ret;
-
- ret = libspotify_playback_seek(seek_ms);
- if (ret < 0)
- return -1;
-
- return ret;
-}
-
-struct input_definition input_libspotify =
-{
- .name = "libspotify",
- .type = INPUT_TYPE_LIBSPOTIFY,
- .disabled = 0,
- .init = init,
- .setup = setup,
- .stop = stop,
- .seek = seek,
-};
-
-
-// No-op for libspotify since it doesn't support logging in with the web api token
-static int
-login_token(const char *username, const char *token, const char **errmsg)
-{
- return 0;
-}
-
-static void
-status_get(struct spotify_status *status)
-{
- struct spotify_status_info info = { 0 };
-
- libspotify_status_info_get(&info);
-
- status->installed = info.libspotify_installed;
- status->logged_in = info.libspotify_logged_in;
- status->has_podcast_support = false;
- snprintf(status->username, sizeof(status->username), "%s", info.libspotify_user);
-}
-
-struct spotify_backend spotify_libspotify =
-{
- .init = libspotify_init,
- .deinit = libspotify_deinit,
- .login = libspotify_login,
- .login_token = login_token,
- .logout = libspotify_logout,
- .relogin = libspotify_relogin,
- .uri_register = libspotify_uri_register,
- .status_get = status_get,
-};
-
diff --git a/src/library/spotify_webapi.c b/src/library/spotify_webapi.c
index 88944f06..0ac89ab4 100644
--- a/src/library/spotify_webapi.c
+++ b/src/library/spotify_webapi.c
@@ -1621,9 +1621,6 @@ track_add(struct spotify_track *track, struct spotify_album *album, const char *
free_mfi(&mfi, 1);
}
- // This is only required for the libspotify backend
- spotify_uri_register(track->uri);
-
if (album && album->uri)
cache_artwork_ping(track->uri, album->mtime, 0);
else
@@ -2053,9 +2050,7 @@ initscan(void)
/*
* Check that the playback Spotify backend can log in, so we don't add tracks
- * to the library that can't be played. Also, libspotify needs to be logged in
- * before before scanning tracks from the web since scanned tracks need to be
- * registered for playback.
+ * to the library that can't be played.
*/
ret = spotify_relogin();
if (ret < 0)
diff --git a/src/misc.c b/src/misc.c
index 9dfdfddc..bff8ffed 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -76,9 +76,6 @@ static char *buildopts[] =
#ifdef SPOTIFY_LIBRESPOTC
"librespot-c",
#endif
-#ifdef SPOTIFY_LIBSPOTIFY
- "libspotify",
-#endif
#ifdef LASTFM
"LastFM",
#else