mirror of
https://github.com/strawberrymusicplayer/strawberry
synced 2024-12-17 02:59:03 +01:00
Make spotify refresh login
This commit is contained in:
parent
d7661f0964
commit
9210fdee0d
@ -29,6 +29,7 @@
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QDateTime>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
@ -47,6 +48,7 @@
|
||||
#include "core/logging.h"
|
||||
#include "core/song.h"
|
||||
#include "core/utilities.h"
|
||||
#include "core/timeconstants.h"
|
||||
#include "internet/localredirectserver.h"
|
||||
#include "albumcoverfetcher.h"
|
||||
#include "jsoncoverprovider.h"
|
||||
@ -61,15 +63,26 @@ const char *SpotifyCoverProvider::kClientSecretB64 = "N2ZlMDMxODk1NTBlNDE3ZGI1ZW
|
||||
const char *SpotifyCoverProvider::kApiUrl = "https://api.spotify.com/v1";
|
||||
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;
|
||||
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();
|
||||
|
||||
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() {
|
||||
@ -129,12 +142,20 @@ void SpotifyCoverProvider::Authenticate() {
|
||||
void SpotifyCoverProvider::Deauthenticate() {
|
||||
|
||||
access_token_.clear();
|
||||
refresh_token_.clear();
|
||||
expires_in_ = 0;
|
||||
login_time_ = 0;
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.remove("access_token");
|
||||
s.remove("refresh_token");
|
||||
s.remove("expires_in");
|
||||
s.remove("login_time");
|
||||
s.endGroup();
|
||||
|
||||
refresh_login_timer_.stop();
|
||||
|
||||
}
|
||||
|
||||
void SpotifyCoverProvider::RedirectArrived() {
|
||||
@ -148,13 +169,16 @@ void SpotifyCoverProvider::RedirectArrived() {
|
||||
if (url_query.hasQueryItem("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);
|
||||
redirect_url.setPort(server_->url().port());
|
||||
RequestAccessToken(url, redirect_url);
|
||||
RequestAccessToken(code, redirect_url);
|
||||
}
|
||||
else {
|
||||
AuthError(tr("Redirect missing token code!"));
|
||||
AuthError(tr("Redirect missing token code or state!"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -171,48 +195,44 @@ 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);
|
||||
|
||||
if (url.hasQuery() && url_query.hasQueryItem("code") && url_query.hasQueryItem("state")) {
|
||||
|
||||
QString code = url_query.queryItemValue("code");
|
||||
QString state = url_query.queryItemValue("state");
|
||||
|
||||
const ParamList params = ParamList() << Param("client_id", QByteArray::fromBase64(kClientIDB64))
|
||||
<< Param("client_secret", QByteArray::fromBase64(kClientSecretB64))
|
||||
<< Param("grant_type", "authorization_code")
|
||||
<< Param("code", code)
|
||||
<< Param("redirect_uri", redirect_url.toString());
|
||||
|
||||
QUrlQuery new_url_query;
|
||||
for (const Param ¶m : params) {
|
||||
new_url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
|
||||
}
|
||||
|
||||
QUrl new_url(kOAuthAccessTokenUrl);
|
||||
QNetworkRequest req = QNetworkRequest(new_url);
|
||||
req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
QString auth_header_data = QByteArray::fromBase64(kClientIDB64) + QString(":") + QByteArray::fromBase64(kClientSecretB64);
|
||||
req.setRawHeader("Authorization", "Basic " + auth_header_data.toUtf8().toBase64());
|
||||
|
||||
QByteArray query = new_url_query.toString(QUrl::FullyEncoded).toUtf8();
|
||||
|
||||
QNetworkReply *reply = network_->post(req, query);
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(HandleLoginSSLErrors(QList<QSslError>)));
|
||||
connect(reply, &QNetworkReply::finished, [=] { AccessTokenRequestFinished(reply); });
|
||||
ParamList params = ParamList() << Param("client_id", QByteArray::fromBase64(kClientIDB64))
|
||||
<< Param("client_secret", QByteArray::fromBase64(kClientSecretB64));
|
||||
|
||||
if (!code.isEmpty() && !redirect_url.isEmpty()) {
|
||||
params << Param("grant_type", "authorization_code");
|
||||
params << Param("code", code);
|
||||
params << Param("redirect_uri", redirect_url.toString());
|
||||
}
|
||||
else if (!refresh_token_.isEmpty() && is_enabled()) {
|
||||
params << Param("grant_type", "refresh_token");
|
||||
params << Param("refresh_token", refresh_token_);
|
||||
}
|
||||
|
||||
else {
|
||||
AuthError(tr("Redirect from Spotify is missing query items code or state."));
|
||||
return;
|
||||
}
|
||||
|
||||
QUrlQuery new_url_query;
|
||||
for (const Param ¶m : params) {
|
||||
new_url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
|
||||
}
|
||||
|
||||
QUrl new_url(kOAuthAccessTokenUrl);
|
||||
QNetworkRequest req = QNetworkRequest(new_url);
|
||||
req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
QString auth_header_data = QByteArray::fromBase64(kClientIDB64) + QString(":") + QByteArray::fromBase64(kClientSecretB64);
|
||||
req.setRawHeader("Authorization", "Basic " + auth_header_data.toUtf8().toBase64());
|
||||
|
||||
QByteArray query = new_url_query.toString(QUrl::FullyEncoded).toUtf8();
|
||||
|
||||
QNetworkReply *reply = network_->post(req, query);
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(HandleLoginSSLErrors(QList<QSslError>)));
|
||||
connect(reply, &QNetworkReply::finished, [=] { AccessTokenRequestFinished(reply); });
|
||||
|
||||
}
|
||||
|
||||
void SpotifyCoverProvider::HandleLoginSSLErrors(QList<QSslError> ssl_errors) {
|
||||
@ -260,7 +280,6 @@ void SpotifyCoverProvider::AccessTokenRequestFinished(QNetworkReply *reply) {
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
qLog(Debug) << data;
|
||||
|
||||
QJsonParseError json_error;
|
||||
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
|
||||
@ -286,19 +305,32 @@ void SpotifyCoverProvider::AccessTokenRequestFinished(QNetworkReply *reply) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json_obj.contains("access_token")) {
|
||||
AuthError("Authentication reply from server is missing access token.", json_obj);
|
||||
if (!json_obj.contains("access_token") || !json_obj.contains("expires_in")) {
|
||||
AuthError("Authentication reply from server is missing access token or expires in.", json_obj);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
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();
|
||||
|
||||
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 AuthenticationSuccess();
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <QSslError>
|
||||
#include <QJsonValue>
|
||||
#include <QJsonObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include "jsoncoverprovider.h"
|
||||
|
||||
@ -55,11 +56,11 @@ class SpotifyCoverProvider : public JsonCoverProvider {
|
||||
private slots:
|
||||
void HandleLoginSSLErrors(QList<QSslError> ssl_errors);
|
||||
void RedirectArrived();
|
||||
void RequestAccessToken(const QString code = QString(), const QUrl redirect_url = QUrl());
|
||||
void AccessTokenRequestFinished(QNetworkReply *reply);
|
||||
void HandleSearchReply(QNetworkReply *reply, const int id, const QString &extract);
|
||||
|
||||
private:
|
||||
void RequestAccessToken(const QUrl &url, const QUrl &redirect_url);
|
||||
QByteArray GetReplyData(QNetworkReply *reply);
|
||||
void AuthError(const QString &error = QString(), 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_challenge_;
|
||||
QString access_token_;
|
||||
QString refresh_token_;
|
||||
quint64 expires_in_;
|
||||
quint64 login_time_;
|
||||
QTimer refresh_login_timer_;
|
||||
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user