1
0
mirror of https://github.com/strawberrymusicplayer/strawberry synced 2024-12-17 11:10:31 +01:00

Make spotify refresh login

This commit is contained in:
Jonas Kvinge 2020-05-10 17:10:20 +02:00
parent d7661f0964
commit 9210fdee0d
2 changed files with 83 additions and 46 deletions

View File

@ -29,6 +29,7 @@
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <QUrlQuery> #include <QUrlQuery>
#include <QDateTime>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QNetworkRequest> #include <QNetworkRequest>
#include <QNetworkReply> #include <QNetworkReply>
@ -47,6 +48,7 @@
#include "core/logging.h" #include "core/logging.h"
#include "core/song.h" #include "core/song.h"
#include "core/utilities.h" #include "core/utilities.h"
#include "core/timeconstants.h"
#include "internet/localredirectserver.h" #include "internet/localredirectserver.h"
#include "albumcoverfetcher.h" #include "albumcoverfetcher.h"
#include "jsoncoverprovider.h" #include "jsoncoverprovider.h"
@ -61,15 +63,26 @@ const char *SpotifyCoverProvider::kClientSecretB64 = "N2ZlMDMxODk1NTBlNDE3ZGI1ZW
const char *SpotifyCoverProvider::kApiUrl = "https://api.spotify.com/v1"; const char *SpotifyCoverProvider::kApiUrl = "https://api.spotify.com/v1";
const int SpotifyCoverProvider::kLimit = 10; const int SpotifyCoverProvider::kLimit = 10;
SpotifyCoverProvider::SpotifyCoverProvider(Application *app, QObject *parent) : JsonCoverProvider("Spotify", true, true, 2.5, true, true, app, parent), network_(new NetworkAccessManager(this)), server_(nullptr) { SpotifyCoverProvider::SpotifyCoverProvider(Application *app, QObject *parent) : JsonCoverProvider("Spotify", true, true, 2.5, true, true, app, parent), network_(new NetworkAccessManager(this)), server_(nullptr), expires_in_(0), login_time_(0) {
refresh_login_timer_.setSingleShot(true);
connect(&refresh_login_timer_, SIGNAL(timeout()), SLOT(RequestAccessToken()));
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
if (s.contains("access_token")) {
access_token_ = s.value("access_token").toString(); access_token_ = s.value("access_token").toString();
} refresh_token_ = s.value("refresh_token").toString();
expires_in_ = s.value("expires_in").toLongLong();
login_time_ = s.value("login_time").toLongLong();
s.endGroup(); s.endGroup();
if (!refresh_token_.isEmpty()) {
qint64 time = expires_in_ - (QDateTime::currentDateTime().toTime_t() - login_time_);
if (time < 6) time = 6;
refresh_login_timer_.setInterval(time * kMsecPerSec);
refresh_login_timer_.start();
}
} }
void SpotifyCoverProvider::Authenticate() { void SpotifyCoverProvider::Authenticate() {
@ -129,12 +142,20 @@ void SpotifyCoverProvider::Authenticate() {
void SpotifyCoverProvider::Deauthenticate() { void SpotifyCoverProvider::Deauthenticate() {
access_token_.clear(); access_token_.clear();
refresh_token_.clear();
expires_in_ = 0;
login_time_ = 0;
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.remove("access_token"); s.remove("access_token");
s.remove("refresh_token");
s.remove("expires_in");
s.remove("login_time");
s.endGroup(); s.endGroup();
refresh_login_timer_.stop();
} }
void SpotifyCoverProvider::RedirectArrived() { void SpotifyCoverProvider::RedirectArrived() {
@ -148,13 +169,16 @@ void SpotifyCoverProvider::RedirectArrived() {
if (url_query.hasQueryItem("error")) { if (url_query.hasQueryItem("error")) {
AuthError(QUrlQuery(url).queryItemValue("error")); AuthError(QUrlQuery(url).queryItemValue("error"));
} }
else if (url_query.hasQueryItem("code")) { else if (url_query.hasQueryItem("code") && url_query.hasQueryItem("state")) {
qLog(Debug) << "Spotify: Authorization URL Received" << url;
QString code = url_query.queryItemValue("code");
QString state = url_query.queryItemValue("state");
QUrl redirect_url(kOAuthRedirectUrl); QUrl redirect_url(kOAuthRedirectUrl);
redirect_url.setPort(server_->url().port()); redirect_url.setPort(server_->url().port());
RequestAccessToken(url, redirect_url); RequestAccessToken(code, redirect_url);
} }
else { else {
AuthError(tr("Redirect missing token code!")); AuthError(tr("Redirect missing token code or state!"));
} }
} }
else { else {
@ -171,22 +195,25 @@ void SpotifyCoverProvider::RedirectArrived() {
} }
void SpotifyCoverProvider::RequestAccessToken(const QUrl &url, const QUrl &redirect_url) { void SpotifyCoverProvider::RequestAccessToken(const QString code, const QUrl redirect_url) {
qLog(Debug) << "Spotify: Authorization URL Received" << url; refresh_login_timer_.stop();
QUrlQuery url_query(url); ParamList params = ParamList() << Param("client_id", QByteArray::fromBase64(kClientIDB64))
<< Param("client_secret", QByteArray::fromBase64(kClientSecretB64));
if (url.hasQuery() && url_query.hasQueryItem("code") && url_query.hasQueryItem("state")) { if (!code.isEmpty() && !redirect_url.isEmpty()) {
params << Param("grant_type", "authorization_code");
QString code = url_query.queryItemValue("code"); params << Param("code", code);
QString state = url_query.queryItemValue("state"); params << Param("redirect_uri", redirect_url.toString());
}
const ParamList params = ParamList() << Param("client_id", QByteArray::fromBase64(kClientIDB64)) else if (!refresh_token_.isEmpty() && is_enabled()) {
<< Param("client_secret", QByteArray::fromBase64(kClientSecretB64)) params << Param("grant_type", "refresh_token");
<< Param("grant_type", "authorization_code") params << Param("refresh_token", refresh_token_);
<< Param("code", code) }
<< Param("redirect_uri", redirect_url.toString()); else {
return;
}
QUrlQuery new_url_query; QUrlQuery new_url_query;
for (const Param &param : params) { for (const Param &param : params) {
@ -206,13 +233,6 @@ void SpotifyCoverProvider::RequestAccessToken(const QUrl &url, const QUrl &redir
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(HandleLoginSSLErrors(QList<QSslError>))); connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(HandleLoginSSLErrors(QList<QSslError>)));
connect(reply, &QNetworkReply::finished, [=] { AccessTokenRequestFinished(reply); }); connect(reply, &QNetworkReply::finished, [=] { AccessTokenRequestFinished(reply); });
}
else {
AuthError(tr("Redirect from Spotify is missing query items code or state."));
return;
}
} }
void SpotifyCoverProvider::HandleLoginSSLErrors(QList<QSslError> ssl_errors) { void SpotifyCoverProvider::HandleLoginSSLErrors(QList<QSslError> ssl_errors) {
@ -260,7 +280,6 @@ void SpotifyCoverProvider::AccessTokenRequestFinished(QNetworkReply *reply) {
} }
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();
qLog(Debug) << data;
QJsonParseError json_error; QJsonParseError json_error;
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error); QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
@ -286,19 +305,32 @@ void SpotifyCoverProvider::AccessTokenRequestFinished(QNetworkReply *reply) {
return; return;
} }
if (!json_obj.contains("access_token")) { if (!json_obj.contains("access_token") || !json_obj.contains("expires_in")) {
AuthError("Authentication reply from server is missing access token.", json_obj); AuthError("Authentication reply from server is missing access token or expires in.", json_obj);
return; return;
} }
access_token_ = json_obj["access_token"].toString(); access_token_ = json_obj["access_token"].toString();
if (json_obj.contains("refresh_token")) {
refresh_token_ = json_obj["refresh_token"].toString();
}
expires_in_ = json_obj["expires_in"].toInt();
login_time_ = QDateTime::currentDateTime().toTime_t();
QSettings s; QSettings s;
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("access_token", access_token_); s.setValue("access_token", access_token_);
s.setValue("refresh_token", refresh_token_);
s.setValue("expires_in", expires_in_);
s.setValue("login_time", login_time_);
s.endGroup(); s.endGroup();
qLog(Debug) << "Spotify: Authentication was successful, got access token" << access_token_; if (expires_in_ > 0) {
refresh_login_timer_.setInterval(expires_in_ * kMsecPerSec);
refresh_login_timer_.start();
}
qLog(Debug) << "Spotify: Authentication was successful, got access token" << access_token_ << "expires in" << expires_in_;
emit AuthenticationComplete(true); emit AuthenticationComplete(true);
emit AuthenticationSuccess(); emit AuthenticationSuccess();

View File

@ -32,6 +32,7 @@
#include <QSslError> #include <QSslError>
#include <QJsonValue> #include <QJsonValue>
#include <QJsonObject> #include <QJsonObject>
#include <QTimer>
#include "jsoncoverprovider.h" #include "jsoncoverprovider.h"
@ -55,11 +56,11 @@ class SpotifyCoverProvider : public JsonCoverProvider {
private slots: private slots:
void HandleLoginSSLErrors(QList<QSslError> ssl_errors); void HandleLoginSSLErrors(QList<QSslError> ssl_errors);
void RedirectArrived(); void RedirectArrived();
void RequestAccessToken(const QString code = QString(), const QUrl redirect_url = QUrl());
void AccessTokenRequestFinished(QNetworkReply *reply); void AccessTokenRequestFinished(QNetworkReply *reply);
void HandleSearchReply(QNetworkReply *reply, const int id, const QString &extract); void HandleSearchReply(QNetworkReply *reply, const int id, const QString &extract);
private: private:
void RequestAccessToken(const QUrl &url, const QUrl &redirect_url);
QByteArray GetReplyData(QNetworkReply *reply); QByteArray GetReplyData(QNetworkReply *reply);
void AuthError(const QString &error = QString(), const QVariant &debug = QVariant()); void AuthError(const QString &error = QString(), const QVariant &debug = QVariant());
void Error(const QString &error, const QVariant &debug = QVariant()); void Error(const QString &error, const QVariant &debug = QVariant());
@ -83,6 +84,10 @@ class SpotifyCoverProvider : public JsonCoverProvider {
QString code_verifier_; QString code_verifier_;
QString code_challenge_; QString code_challenge_;
QString access_token_; QString access_token_;
QString refresh_token_;
quint64 expires_in_;
quint64 login_time_;
QTimer refresh_login_timer_;
}; };