Merge pull request #1339 from owntone/spotify_disconnect1

Fix for Spotify disconnect issues
This commit is contained in:
ejurgensen 2021-11-08 23:35:40 +01:00 committed by GitHub
commit 4c5217d3d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 174 additions and 133 deletions

View File

@ -220,15 +220,12 @@ session_return(struct sp_session *session, enum sp_error err)
commands_exec_end(sp_cmdbase, err);
}
// Rolls back from an error situation. If it is a failed login then the session
// will be closed, but if it just a connection timeout we keep the session, but
// drop the ongoing download.
// Disconnects after an error situation. If it is a failed login then the
// session, otherwise we end download and disconnect.
static void
session_error(struct sp_session *session, enum sp_error err)
{
struct sp_channel *channel = session->now_streaming_channel;
sp_cb.logmsg("Session error: %d\n", err);
sp_cb.logmsg("Session error: %d (occurred before msg %d, queue %d)\n", err, session->msg_type_next, session->msg_type_queued);
session_return(session, err);
@ -238,8 +235,10 @@ session_error(struct sp_session *session, enum sp_error err)
return;
}
channel_free(channel);
channel_free_all(session);
session->now_streaming_channel = NULL;
ap_disconnect(&session->conn);
}

View File

@ -52,7 +52,6 @@ backend_set(void)
/* -------------- Dispatches functions exposed via spotify.h ---------------- */
/* (probably not necessary when libspotify is removed) */
/* Called from other threads than the input thread */
int

View File

@ -51,9 +51,7 @@
struct global_ctx
{
pthread_mutex_t lock;
pthread_cond_t cond;
bool is_initialized;
struct spotify_status status;
struct sp_session *session;
@ -75,6 +73,12 @@ struct download_ctx
static struct global_ctx spotify_ctx;
// Must be initialized statically since we don't have anywhere to do it at
// runtime. We are in the special situation that multiple threads can result in
// calls to initialize(), e.g. input_init() and library init scan, thus it must
// have the lock ready to use to be thread safe.
static pthread_mutex_t spotify_ctx_lock = PTHREAD_MUTEX_INITIALIZER;
static struct media_quality spotify_quality = { 44100, 16, 2, 0 };
@ -138,6 +142,42 @@ postlogin(struct global_ctx *ctx)
return 0;
}
static int
login_stored_cred(struct global_ctx *ctx, const char *username, const char *db_stored_cred)
{
size_t db_stored_cred_len;
uint8_t *stored_cred = NULL;
size_t stored_cred_len;
int ret;
db_stored_cred_len = strlen(db_stored_cred);
stored_cred_len = db_stored_cred_len / 2;
CHECK_NULL(L_SPOTIFY, stored_cred = malloc(stored_cred_len));
hextobin(stored_cred, stored_cred_len, db_stored_cred, db_stored_cred_len);
ctx->session = librespotc_login_stored_cred(username, stored_cred, stored_cred_len);
if (!ctx->session)
{
DPRINTF(E_LOG, L_SPOTIFY, "Error logging into Spotify: %s\n", librespotc_last_errmsg());
goto error;
}
ret = postlogin(ctx);
if (ret < 0)
goto error;
free(stored_cred);
return 0;
error:
free(stored_cred);
if (ctx->session)
librespotc_logout(ctx->session);
ctx->session = NULL;
return -1;
}
// If there is evbuf size is below max, reads from a non-blocking fd until error,
// EAGAIN or evbuf full
static int
@ -246,7 +286,7 @@ hexdump_cb(const char *msg, uint8_t *data, size_t data_len)
}
/* --------------------- Implementation (input thread) ---------------------- */
/* ----------------------- libresport-c initialization ---------------------- */
struct sp_callbacks callbacks = {
.https_get = https_get_cb,
@ -259,16 +299,68 @@ struct sp_callbacks callbacks = {
.logmsg = logmsg_cb,
};
// Called from main thread as part of player_init, or from library thread as
// part of relogin. Caller must use mutex for thread safety.
static int
initialize(struct global_ctx *ctx)
{
struct sp_sysinfo sysinfo;
cfg_t *spotify_cfg;
int ret;
spotify_cfg = cfg_getsec(cfg, "spotify");
if (cfg_getbool(spotify_cfg, "use_libspotify"))
return -1;
if (ctx->is_initialized)
return 0;
snprintf(sysinfo.client_name, sizeof(sysinfo.client_name), PACKAGE_NAME);
snprintf(sysinfo.client_version, sizeof(sysinfo.client_version), PACKAGE_VERSION);
snprintf(sysinfo.client_build_id, sizeof(sysinfo.client_build_id), "0");
snprintf(sysinfo.device_id, sizeof(sysinfo.device_id), "%" PRIx64, libhash); // TODO use a UUID instead
ret = librespotc_init(&sysinfo, &callbacks);
if (ret < 0)
{
DPRINTF(E_LOG, L_SPOTIFY, "Error initializing Spotify: %s\n", librespotc_last_errmsg());
goto error;
}
switch (cfg_getint(spotify_cfg, "bitrate"))
{
case 1:
ctx->bitrate_preferred = SP_BITRATE_96;
break;
case 2:
ctx->bitrate_preferred = SP_BITRATE_160;
break;
case 3:
ctx->bitrate_preferred = SP_BITRATE_320;
break;
default:
ctx->bitrate_preferred = SP_BITRATE_ANY;
}
ctx->is_initialized = true;
return 0;
error:
ctx->is_initialized = false;
return -1;
}
/* --------------------- Implementation (input thread) ---------------------- */
static int64_t
download_seek(void *arg, int64_t offset, enum transcode_seek_type type)
{
struct global_ctx *ctx = &spotify_ctx;
struct download_ctx *download = arg;
int64_t out;
int ret;
pthread_mutex_lock(&ctx->lock);
switch (type)
{
case XCODE_SEEK_SIZE:
@ -290,8 +382,6 @@ download_seek(void *arg, int64_t offset, enum transcode_seek_type type)
goto error;
}
pthread_mutex_unlock(&ctx->lock);
DPRINTF(E_DBG, L_SPOTIFY, "Seek to offset %" PRIi64 " requested, type %d, returning %" PRIi64 "\n", offset, type, out);
return out;
@ -299,7 +389,6 @@ download_seek(void *arg, int64_t offset, enum transcode_seek_type type)
error:
DPRINTF(E_WARN, L_SPOTIFY, "Seek error\n");
pthread_mutex_unlock(&ctx->lock);
return -1;
}
@ -368,12 +457,11 @@ download_new(int fd, uint32_t len_ms, size_t len_bytes)
static int
stop(struct input_source *source)
{
struct global_ctx *ctx = &spotify_ctx;
struct download_ctx *download = source->input_ctx;
DPRINTF(E_DBG, L_SPOTIFY, "stop()\n");
pthread_mutex_lock(&ctx->lock);
pthread_mutex_lock(&spotify_ctx_lock);
download_free(download);
@ -383,7 +471,7 @@ stop(struct input_source *source)
source->input_ctx = NULL;
source->evbuf = NULL;
pthread_mutex_unlock(&ctx->lock);
pthread_mutex_unlock(&spotify_ctx_lock);
return 0;
}
@ -400,7 +488,7 @@ setup(struct input_source *source)
DPRINTF(E_DBG, L_SPOTIFY, "setup()\n");
pthread_mutex_lock(&ctx->lock);
pthread_mutex_lock(&spotify_ctx_lock);
fd = librespotc_open(source->path, ctx->session);
if (fd < 0)
@ -438,11 +526,11 @@ setup(struct input_source *source)
if (ret < 0)
goto error;
pthread_mutex_unlock(&ctx->lock);
pthread_mutex_unlock(&spotify_ctx_lock);
return 0;
error:
pthread_mutex_unlock(&ctx->lock);
pthread_mutex_unlock(&spotify_ctx_lock);
stop(source);
return -1;
@ -507,120 +595,41 @@ static int
seek(struct input_source *source, int seek_ms)
{
struct download_ctx *download = source->input_ctx;
int ret;
pthread_mutex_lock(&spotify_ctx_lock);
// This will make transcode call back to download_seek(), but with a byte
// offset instead of a ms position, which is what librespot-c requires
return transcode_seek(download->xcode, seek_ms);
ret = transcode_seek(download->xcode, seek_ms);
pthread_mutex_unlock(&spotify_ctx_lock);
return ret;
}
static int
login_stored_cred(struct global_ctx *ctx, const char *username, const char *db_stored_cred)
{
size_t db_stored_cred_len;
uint8_t *stored_cred = NULL;
size_t stored_cred_len;
int ret;
db_stored_cred_len = strlen(db_stored_cred);
stored_cred_len = db_stored_cred_len / 2;
CHECK_NULL(L_SPOTIFY, stored_cred = malloc(stored_cred_len));
hextobin(stored_cred, stored_cred_len, db_stored_cred, db_stored_cred_len);
ctx->session = librespotc_login_stored_cred(username, stored_cred, stored_cred_len);
if (!ctx->session)
{
DPRINTF(E_LOG, L_SPOTIFY, "Error logging into Spotify: %s\n", librespotc_last_errmsg());
goto error;
}
ret = postlogin(ctx);
if (ret < 0)
goto error;
free(stored_cred);
return 0;
error:
free(stored_cred);
if (ctx->session)
librespotc_logout(ctx->session);
ctx->session = NULL;
return -1;
}
static int
init(void)
{
struct sp_sysinfo sysinfo;
cfg_t *spotify_cfg;
char *username = NULL;
char *db_stored_cred = NULL;
int ret;
spotify_cfg = cfg_getsec(cfg, "spotify");
pthread_mutex_lock(&spotify_ctx_lock);
if (cfg_getbool(spotify_cfg, "use_libspotify"))
return -1;
ret = initialize(&spotify_ctx);
CHECK_ERR(L_SPOTIFY, mutex_init(&spotify_ctx.lock));
CHECK_ERR(L_SPOTIFY, pthread_cond_init(&spotify_ctx.cond, NULL));
pthread_mutex_unlock(&spotify_ctx_lock);
snprintf(sysinfo.client_name, sizeof(sysinfo.client_name), PACKAGE_NAME);
snprintf(sysinfo.client_version, sizeof(sysinfo.client_version), PACKAGE_VERSION);
snprintf(sysinfo.client_build_id, sizeof(sysinfo.client_build_id), "0");
snprintf(sysinfo.device_id, sizeof(sysinfo.device_id), "%" PRIx64, libhash); // TODO use a UUID instead
ret = librespotc_init(&sysinfo, &callbacks);
if (ret < 0)
{
DPRINTF(E_LOG, L_SPOTIFY, "Error initializing Spotify: %s\n", librespotc_last_errmsg());
goto error;
}
switch (cfg_getint(spotify_cfg, "bitrate"))
{
case 1:
spotify_ctx.bitrate_preferred = SP_BITRATE_96;
break;
case 2:
spotify_ctx.bitrate_preferred = SP_BITRATE_160;
break;
case 3:
spotify_ctx.bitrate_preferred = SP_BITRATE_320;
break;
default:
spotify_ctx.bitrate_preferred = SP_BITRATE_ANY;
}
// Re-login if we have stored credentials
db_admin_get(&username, "spotify_username");
db_admin_get(&db_stored_cred, "spotify_stored_cred");
if (username && db_stored_cred)
{
ret = login_stored_cred(&spotify_ctx, username, db_stored_cred);
if (ret < 0)
goto error;
}
free(username);
free(db_stored_cred);
return 0;
error:
free(username);
free(db_stored_cred);
return -1;
return ret;
}
static void
deinit(void)
{
pthread_mutex_lock(&spotify_ctx_lock);
librespotc_deinit();
CHECK_ERR(L_SPOTIFY, pthread_cond_destroy(&spotify_ctx.cond));
CHECK_ERR(L_SPOTIFY, pthread_mutex_destroy(&spotify_ctx.lock));
pthread_mutex_unlock(&spotify_ctx_lock);
}
struct input_definition input_spotify =
@ -646,7 +655,7 @@ login(const char *username, const char *password, const char **errmsg)
struct global_ctx *ctx = &spotify_ctx;
int ret;
pthread_mutex_lock(&ctx->lock);
pthread_mutex_lock(&spotify_ctx_lock);
ctx->session = librespotc_login_password(username, password);
if (!ctx->session)
@ -656,7 +665,7 @@ login(const char *username, const char *password, const char **errmsg)
if (ret < 0)
goto error;
pthread_mutex_unlock(&ctx->lock);
pthread_mutex_unlock(&spotify_ctx_lock);
return 0;
@ -668,7 +677,7 @@ login(const char *username, const char *password, const char **errmsg)
if (errmsg)
*errmsg = librespotc_last_errmsg();
pthread_mutex_unlock(&ctx->lock);
pthread_mutex_unlock(&spotify_ctx_lock);
return -1;
}
@ -679,7 +688,7 @@ login_token(const char *username, const char *token, const char **errmsg)
struct global_ctx *ctx = &spotify_ctx;
int ret;
pthread_mutex_lock(&ctx->lock);
pthread_mutex_lock(&spotify_ctx_lock);
ctx->session = librespotc_login_token(username, token);
if (!ctx->session)
@ -689,7 +698,7 @@ login_token(const char *username, const char *token, const char **errmsg)
if (ret < 0)
goto error;
pthread_mutex_unlock(&ctx->lock);
pthread_mutex_unlock(&spotify_ctx_lock);
return 0;
@ -701,7 +710,7 @@ login_token(const char *username, const char *token, const char **errmsg)
if (errmsg)
*errmsg = librespotc_last_errmsg();
pthread_mutex_unlock(&ctx->lock);
pthread_mutex_unlock(&spotify_ctx_lock);
return -1;
}
@ -714,20 +723,52 @@ logout(void)
db_admin_delete("spotify_username");
db_admin_delete("spotify_stored_cred");
pthread_mutex_lock(&ctx->lock);
pthread_mutex_lock(&spotify_ctx_lock);
librespotc_logout(ctx->session);
ctx->session = NULL;
memset(&ctx->status, 0, sizeof(ctx->status));
pthread_mutex_unlock(&ctx->lock);
pthread_mutex_unlock(&spotify_ctx_lock);
}
static int
relogin(void)
{
return 0; // re-login is only relevant for libspotify, here it is just a no-op
struct global_ctx *ctx = &spotify_ctx;
char *username = NULL;
char *db_stored_cred = NULL;
int ret;
pthread_mutex_lock(&spotify_ctx_lock);
ret = initialize(ctx);
if (ret < 0)
goto error;
// Re-login if we have stored credentials
db_admin_get(&username, "spotify_username");
db_admin_get(&db_stored_cred, "spotify_stored_cred");
if (username && db_stored_cred)
{
ret = login_stored_cred(ctx, username, db_stored_cred);
if (ret < 0)
goto error;
}
free(username);
free(db_stored_cred);
pthread_mutex_unlock(&spotify_ctx_lock);
return 0;
error:
free(username);
free(db_stored_cred);
pthread_mutex_unlock(&spotify_ctx_lock);
return -1;
}
static void
@ -735,14 +776,14 @@ status_get(struct spotify_status *status)
{
struct global_ctx *ctx = &spotify_ctx;
pthread_mutex_lock(&ctx->lock);
pthread_mutex_lock(&spotify_ctx_lock);
memcpy(status->username, ctx->status.username, sizeof(status->username));
status->logged_in = ctx->status.logged_in;
status->installed = true;
status->has_podcast_support = true;
pthread_mutex_unlock(&ctx->lock);
pthread_mutex_unlock(&spotify_ctx_lock);
}
struct spotify_backend spotify_librespotc =

View File

@ -2039,14 +2039,16 @@ initscan(void)
spotify_saved_plid = 0;
/*
* libspotify needs to be logged in before before scanning tracks from the web
* since scanned tracks need to be registered for playback
* 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.
*/
ret = spotify_relogin();
if (ret < 0)
{
DPRINTF(E_LOG, L_SPOTIFY, "libspotify-login failed. In order to use Spotify, "
"provide valid credentials for libspotify by visiting http://owntone.local:3689\n");
DPRINTF(E_LOG, L_SPOTIFY, "Spotify playback library could not log in. In order to use Spotify, "
"provide valid credentials by visiting http://owntone.local:3689\n");
db_spotify_purge();