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