Improve album cover searching and cover manager, use HttpStatusCodeAttribute and QSslError for services

- Improve album cover manager
- Change art_automatic and art_manual to QUrl
- Refresh collection album covers when new album covers are fetched
- Fix automatic album cover searching for local files outside of the collection
- Make all Json services check HttpStatusCodeAttribute
- Show detailed SSL errors for Subsonic, Tidal and Qobuz
This commit is contained in:
Jonas Kvinge
2019-07-07 21:14:24 +02:00
parent c92a7967ea
commit 65780e1672
101 changed files with 1531 additions and 1239 deletions

View File

@@ -77,6 +77,7 @@ QNetworkReply *QobuzBaseRequest::CreateRequest(const QString &ressource_name, co
req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply *reply = network_->get(req);
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(HandleSSLErrors(QList<QSslError>)));
replies_ << reply;
//qLog(Debug) << "Qobuz: Sending request" << url;
@@ -85,7 +86,15 @@ QNetworkReply *QobuzBaseRequest::CreateRequest(const QString &ressource_name, co
}
QByteArray QobuzBaseRequest::GetReplyData(QNetworkReply *reply, QString &error) {
void QobuzBaseRequest::HandleSSLErrors(QList<QSslError> ssl_errors) {
for (QSslError &ssl_error : ssl_errors) {
Error(ssl_error.errorString());
}
}
QByteArray QobuzBaseRequest::GetReplyData(QNetworkReply *reply) {
if (replies_.contains(reply)) {
replies_.removeAll(reply);
@@ -94,39 +103,38 @@ QByteArray QobuzBaseRequest::GetReplyData(QNetworkReply *reply, QString &error)
QByteArray data;
if (reply->error() == QNetworkReply::NoError) {
int http_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (http_code == 200) {
data = reply->readAll();
}
else {
error = Error(QString("Received HTTP code %1").arg(http_code));
}
if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) {
data = reply->readAll();
}
else {
if (reply->error() < 200) {
if (reply->error() != QNetworkReply::NoError && reply->error() < 200) {
// This is a network error, there is nothing more to do.
error = Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
}
else {
// See if there is Json data containing "status", "code" and "message" - then use that instead.
data = reply->readAll();
QString error;
QJsonParseError parse_error;
QJsonDocument json_doc = QJsonDocument::fromJson(data, &parse_error);
QString failure_reason;
if (parse_error.error == QJsonParseError::NoError && !json_doc.isNull() && !json_doc.isEmpty() && json_doc.isObject()) {
QJsonObject json_obj = json_doc.object();
if (!json_obj.isEmpty() && json_obj.contains("status") && json_obj.contains("code") && json_obj.contains("message")) {
QString status = json_obj["status"].toString();
int code = json_obj["code"].toInt();
QString message = json_obj["message"].toString();
failure_reason = QString("%1 (%2)").arg(message).arg(code);
error = QString("%1 (%2)").arg(message).arg(code);
}
}
if (failure_reason.isEmpty()) {
failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
if (error.isEmpty()) {
if (reply->error() != QNetworkReply::NoError) {
error = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
}
else {
error = QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
}
}
error = Error(failure_reason);
Error(error);
}
return QByteArray();
}
@@ -135,29 +143,29 @@ QByteArray QobuzBaseRequest::GetReplyData(QNetworkReply *reply, QString &error)
}
QJsonObject QobuzBaseRequest::ExtractJsonObj(QByteArray &data, QString &error) {
QJsonObject QobuzBaseRequest::ExtractJsonObj(QByteArray &data) {
QJsonParseError json_error;
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
if (json_error.error != QJsonParseError::NoError) {
error = Error("Reply from server missing Json data.", data);
Error("Reply from server missing Json data.", data);
return QJsonObject();
}
if (json_doc.isNull() || json_doc.isEmpty()) {
error = Error("Received empty Json document.", data);
Error("Received empty Json document.", data);
return QJsonObject();
}
if (!json_doc.isObject()) {
error = Error("Json document is not an object.", json_doc);
Error("Json document is not an object.", json_doc);
return QJsonObject();
}
QJsonObject json_obj = json_doc.object();
if (json_obj.isEmpty()) {
error = Error("Received empty Json object.", json_doc);
Error("Received empty Json object.", json_doc);
return QJsonObject();
}
@@ -165,18 +173,18 @@ QJsonObject QobuzBaseRequest::ExtractJsonObj(QByteArray &data, QString &error) {
}
QJsonValue QobuzBaseRequest::ExtractItems(QByteArray &data, QString &error) {
QJsonValue QobuzBaseRequest::ExtractItems(QByteArray &data) {
QJsonObject json_obj = ExtractJsonObj(data, error);
QJsonObject json_obj = ExtractJsonObj(data);
if (json_obj.isEmpty()) return QJsonValue();
return ExtractItems(json_obj, error);
return ExtractItems(json_obj);
}
QJsonValue QobuzBaseRequest::ExtractItems(QJsonObject &json_obj, QString &error) {
QJsonValue QobuzBaseRequest::ExtractItems(QJsonObject &json_obj) {
if (!json_obj.contains("items")) {
error = Error("Json reply is missing items.", json_obj);
Error("Json reply is missing items.", json_obj);
return QJsonArray();
}
QJsonValue json_items = json_obj["items"];
@@ -184,11 +192,12 @@ QJsonValue QobuzBaseRequest::ExtractItems(QJsonObject &json_obj, QString &error)
}
QString QobuzBaseRequest::Error(QString error, QVariant debug) {
QString QobuzBaseRequest::ErrorsToHTML(const QStringList &errors) {
qLog(Error) << "Qobuz:" << error;
if (debug.isValid()) qLog(Debug) << debug;
return error;
QString error_html;
for (const QString &error : errors) {
error_html += error + "<br />";
}
return error_html;
}

View File

@@ -71,12 +71,13 @@ class QobuzBaseRequest : public QObject {
typedef QList<EncodedParam> EncodedParamList;
QNetworkReply *CreateRequest(const QString &ressource_name, const QList<Param> &params_provided);
QByteArray GetReplyData(QNetworkReply *reply, QString &error);
QJsonObject ExtractJsonObj(QByteArray &data, QString &error);
QJsonValue ExtractItems(QByteArray &data, QString &error);
QJsonValue ExtractItems(QJsonObject &json_obj, QString &error);
QByteArray GetReplyData(QNetworkReply *reply);
QJsonObject ExtractJsonObj(QByteArray &data);
QJsonValue ExtractItems(QByteArray &data);
QJsonValue ExtractItems(QJsonObject &json_obj);
virtual QString Error(QString error, QVariant debug = QVariant());
virtual void Error(const QString &error, const QVariant &debug = QVariant()) = 0;
QString ErrorsToHTML(const QStringList &errors);
QString api_url() { return QString(kApiUrl); }
QString app_id() { return service_->app_id(); }
@@ -95,6 +96,9 @@ class QobuzBaseRequest : public QObject {
int max_login_attempts() { return service_->max_login_attempts(); }
int login_attempts() { return service_->login_attempts(); }
private slots:
void HandleSSLErrors(QList<QSslError> ssl_errors);
private:
static const char *kApiUrl;

View File

@@ -154,8 +154,7 @@ void QobuzFavoriteRequest::AddFavoritesReply(QNetworkReply *reply, const Favorit
return;
}
QString error;
QByteArray data = GetReplyData(reply, error);
QByteArray data = GetReplyData(reply);
if (reply->error() != QNetworkReply::NoError) {
return;
@@ -258,8 +257,7 @@ void QobuzFavoriteRequest::RemoveFavoritesReply(QNetworkReply *reply, const Favo
return;
}
QString error;
QByteArray data = GetReplyData(reply, error);
QByteArray data = GetReplyData(reply);
if (reply->error() != QNetworkReply::NoError) {
return;
}
@@ -279,3 +277,10 @@ void QobuzFavoriteRequest::RemoveFavoritesReply(QNetworkReply *reply, const Favo
}
}
void QobuzFavoriteRequest::Error(const QString &error, const QVariant &debug) {
qLog(Error) << "Qobuz:" << error;
if (debug.isValid()) qLog(Debug) << debug;
}

View File

@@ -65,6 +65,7 @@ class QobuzFavoriteRequest : public QobuzBaseRequest {
void RemoveFavoritesReply(QNetworkReply *reply, const FavoriteType type, const SongList &songs);
private:
void Error(const QString &error, const QVariant &debug = QVariant());
QString FavoriteText(const FavoriteType type);
void AddFavorites(const FavoriteType type, const SongList &songs);
void RemoveFavorites(const FavoriteType type, const SongList &songs);

View File

@@ -35,7 +35,8 @@
#include "core/network.h"
#include "core/song.h"
#include "core/timeconstants.h"
#include "organise/organiseformat.h"
#include "core/application.h"
#include "covermanager/albumcoverloader.h"
#include "qobuzservice.h"
#include "qobuzurlhandler.h"
#include "qobuzrequest.h"
@@ -47,10 +48,11 @@ const int QobuzRequest::kMaxConcurrentArtistAlbumsRequests = 3;
const int QobuzRequest::kMaxConcurrentAlbumSongsRequests = 3;
const int QobuzRequest::kMaxConcurrentAlbumCoverRequests = 1;
QobuzRequest::QobuzRequest(QobuzService *service, QobuzUrlHandler *url_handler, NetworkAccessManager *network, QueryType type, QObject *parent)
QobuzRequest::QobuzRequest(QobuzService *service, QobuzUrlHandler *url_handler, Application *app, NetworkAccessManager *network, QueryType type, QObject *parent)
: QobuzBaseRequest(service, network, parent),
service_(service),
url_handler_(url_handler),
app_(app),
network_(network),
type_(type),
query_id_(-1),
@@ -300,8 +302,7 @@ void QobuzRequest::AddSongsSearchRequest(const int offset) {
void QobuzRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested) {
QString error;
QByteArray data = GetReplyData(reply, error);
QByteArray data = GetReplyData(reply);
--artists_requests_active_;
@@ -312,7 +313,7 @@ void QobuzRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_re
return;
}
QJsonObject json_obj = ExtractJsonObj(data, error);
QJsonObject json_obj = ExtractJsonObj(data);
if (json_obj.isEmpty()) {
ArtistsFinishCheck();
return;
@@ -363,7 +364,7 @@ void QobuzRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_re
emit UpdateProgress(query_id_, artists_received_);
}
QJsonValue json_value = ExtractItems(json_obj_artists, error);
QJsonValue json_value = ExtractItems(json_obj_artists);
if (!json_value.isArray()) {
ArtistsFinishCheck();
return;
@@ -496,8 +497,7 @@ void QobuzRequest::ArtistAlbumsReplyReceived(QNetworkReply *reply, const qint64
void QobuzRequest::AlbumsReceived(QNetworkReply *reply, const qint64 artist_id_requested, const int limit_requested, const int offset_requested) {
QString error;
QByteArray data = GetReplyData(reply, error);
QByteArray data = GetReplyData(reply);
if (finished_) return;
@@ -506,7 +506,7 @@ void QobuzRequest::AlbumsReceived(QNetworkReply *reply, const qint64 artist_id_r
return;
}
QJsonObject json_obj = ExtractJsonObj(data, error);
QJsonObject json_obj = ExtractJsonObj(data);
if (json_obj.isEmpty()) {
AlbumsFinishCheck(artist_id_requested);
return;
@@ -559,7 +559,7 @@ void QobuzRequest::AlbumsReceived(QNetworkReply *reply, const qint64 artist_id_r
return;
}
QJsonValue json_value = ExtractItems(json_obj_albums, error);
QJsonValue json_value = ExtractItems(json_obj_albums);
if (!json_value.isArray()) {
AlbumsFinishCheck(artist_id_requested);
return;
@@ -726,8 +726,7 @@ void QobuzRequest::AlbumSongsReplyReceived(QNetworkReply *reply, const qint64 ar
void QobuzRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id_requested, const QString &album_id_requested, const int limit_requested, const int offset_requested, const QString &album_artist_requested, const QString &album_requested) {
QString error;
QByteArray data = GetReplyData(reply, error);
QByteArray data = GetReplyData(reply);
if (finished_) return;
@@ -736,7 +735,7 @@ void QobuzRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id_re
return;
}
QJsonObject json_obj = ExtractJsonObj(data, error);
QJsonObject json_obj = ExtractJsonObj(data);
if (json_obj.isEmpty()) {
SongsFinishCheck(artist_id_requested, album_id_requested, limit_requested, offset_requested, 0, 0, album_artist_requested, album_requested);
return;
@@ -825,7 +824,7 @@ void QobuzRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id_re
return;
}
QJsonValue json_value = ExtractItems(json_obj_tracks, error);
QJsonValue json_value = ExtractItems(json_obj_tracks);
if (!json_value.isArray()) {
SongsFinishCheck(artist_id, album_id, limit_requested, offset_requested, songs_total, 0, album_artist, album);
return;
@@ -1045,7 +1044,7 @@ int QobuzRequest::ParseSong(Song &song, const QJsonObject &json_obj, qint64 arti
song.set_track(track);
song.set_url(url);
song.set_length_nanosec(duration);
song.set_art_automatic(cover_url.toEncoded());
song.set_art_automatic(cover_url);
song.set_comment(copyright);
song.set_directory_id(0);
song.set_filetype(Song::FileType_Stream);
@@ -1082,12 +1081,13 @@ void QobuzRequest::AddAlbumCoverRequest(Song &song) {
return;
}
album_covers_requests_sent_.insertMulti(cover_url, &song);
++album_covers_requested_;
AlbumCoverRequest request;
request.url = cover_url;
request.filename = AlbumCoverFileName(song);
request.filename = app_->album_cover_loader()->CoverFilePath(song.source(), song.effective_albumartist(), song.effective_album(), song.album_id(), QString(), cover_url);
if (request.filename.isEmpty()) return;
album_covers_requests_sent_.insertMulti(cover_url, &song);
++album_covers_requested_;
album_cover_requests_queue_.enqueue(request);
@@ -1132,9 +1132,8 @@ void QobuzRequest::AlbumCoverReceived(QNetworkReply *reply, const QUrl &cover_ur
return;
}
QString error;
if (reply->error() != QNetworkReply::NoError) {
error = Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
album_covers_requests_sent_.remove(cover_url);
AlbumCoverFinishCheck();
return;
@@ -1142,7 +1141,7 @@ void QobuzRequest::AlbumCoverReceived(QNetworkReply *reply, const QUrl &cover_ur
QByteArray data = reply->readAll();
if (data.isEmpty()) {
error = Error(QString("Received empty image data for %1").arg(cover_url.toString()));
Error(QString("Received empty image data for %1").arg(cover_url.toString()));
album_covers_requests_sent_.remove(cover_url);
AlbumCoverFinishCheck();
return;
@@ -1151,57 +1150,26 @@ void QobuzRequest::AlbumCoverReceived(QNetworkReply *reply, const QUrl &cover_ur
QImage image;
if (image.loadFromData(data)) {
QDir dir;
if (dir.mkpath(service_->CoverCacheDir())) {
QString filepath(service_->CoverCacheDir() + "/" + filename);
if (image.save(filepath, "JPG")) {
while (album_covers_requests_sent_.contains(cover_url)) {
Song *song = album_covers_requests_sent_.take(cover_url);
song->set_art_automatic(filepath);
}
if (image.save(filename, "JPG")) {
while (album_covers_requests_sent_.contains(cover_url)) {
Song *song = album_covers_requests_sent_.take(cover_url);
QUrl cover_url;
cover_url.setScheme("file");
cover_url.setPath(filename);
song->set_art_automatic(cover_url);
}
}
}
else {
album_covers_requests_sent_.remove(cover_url);
error = Error(QString("Error decoding image data from %1").arg(cover_url.toString()));
Error(QString("Error decoding image data from %1").arg(cover_url.toString()));
}
AlbumCoverFinishCheck();
}
QString QobuzRequest::AlbumCoverFileName(const Song &song) {
QString artist = song.effective_albumartist();
QString album = song.effective_album();
QString title = song.title();
artist.remove('/');
album.remove('/');
title.remove('/');
QString filename = artist + "-" + album + ".jpg";
filename = filename.toLower();
filename.replace(' ', '-');
filename.replace("--", "-");
filename.replace(230, "ae");
filename.replace(198, "AE");
filename.replace(246, 'o');
filename.replace(248, 'o');
filename.replace(214, 'O');
filename.replace(216, 'O');
filename.replace(228, 'a');
filename.replace(229, 'a');
filename.replace(196, 'A');
filename.replace(197, 'A');
filename.remove(OrganiseFormat::kValidFatCharacters);
return filename;
}
void QobuzRequest::AlbumCoverFinishCheck() {
if (!album_cover_requests_queue_.isEmpty() && album_covers_requests_active_ < kMaxConcurrentAlbumCoverRequests)
@@ -1245,28 +1213,24 @@ void QobuzRequest::FinishCheck() {
if (songs_.isEmpty() && errors_.isEmpty())
emit Results(query_id_, songs_, tr("Unknown error"));
else
emit Results(query_id_, songs_, errors_);
emit Results(query_id_, songs_, ErrorsToHTML(errors_));
}
}
}
QString QobuzRequest::Error(QString error, QVariant debug) {
qLog(Error) << "Qobuz:" << error;
if (debug.isValid()) qLog(Debug) << debug;
void QobuzRequest::Error(const QString &error, const QVariant &debug) {
if (!error.isEmpty()) {
errors_ += error;
errors_ += "<br />";
errors_ << error;
qLog(Error) << "Qobuz:" << error;
}
if (debug.isValid()) qLog(Debug) << debug;
FinishCheck();
return error;
}
void QobuzRequest::Warn(QString error, QVariant debug) {
void QobuzRequest::Warn(const QString &error, const QVariant &debug) {
qLog(Error) << "Qobuz:" << error;
if (debug.isValid()) qLog(Debug) << debug;

View File

@@ -31,6 +31,7 @@
#include <QMultiMap>
#include <QQueue>
#include <QString>
#include <QStringList>
#include <QUrl>
#include <QNetworkReply>
#include <QJsonObject>
@@ -40,6 +41,7 @@
#include "core/song.h"
#include "qobuzbaserequest.h"
class Application;
class NetworkAccessManager;
class QobuzService;
class QobuzUrlHandler;
@@ -49,7 +51,7 @@ class QobuzRequest : public QobuzBaseRequest {
public:
QobuzRequest(QobuzService *service, QobuzUrlHandler *url_handler, NetworkAccessManager *network, QueryType type, QObject *parent);
QobuzRequest(QobuzService *service, QobuzUrlHandler *url_handler, Application *app, NetworkAccessManager *network, QueryType type, QObject *parent);
~QobuzRequest();
void ReloadSettings();
@@ -141,8 +143,8 @@ class QobuzRequest : public QobuzBaseRequest {
void AlbumCoverFinishCheck();
void FinishCheck();
void Warn(QString error, QVariant debug = QVariant());
QString Error(QString error, QVariant debug = QVariant());
void Warn(const QString &error, const QVariant &debug = QVariant());
void Error(const QString &error, const QVariant &debug = QVariant());
static const int kMaxConcurrentArtistsRequests;
static const int kMaxConcurrentAlbumsRequests;
@@ -153,6 +155,7 @@ class QobuzRequest : public QobuzBaseRequest {
QobuzService *service_;
QobuzUrlHandler *url_handler_;
Application *app_;
NetworkAccessManager *network_;
QueryType type_;
@@ -193,7 +196,7 @@ class QobuzRequest : public QobuzBaseRequest {
int album_covers_received_;
SongList songs_;
QString errors_;
QStringList errors_;
bool no_results_;
QList<QNetworkReply*> album_cover_replies_;

View File

@@ -23,7 +23,6 @@
#include <memory>
#include <QObject>
#include <QStandardPaths>
#include <QDesktopServices>
#include <QCryptographicHash>
#include <QByteArray>
@@ -213,10 +212,6 @@ void QobuzService::ReloadSettings() {
}
QString QobuzService::CoverCacheDir() {
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/qobuzalbumcovers";
}
void QobuzService::SendLogin() {
SendLogin(app_id_, username_, password_);
}
@@ -224,6 +219,7 @@ void QobuzService::SendLogin() {
void QobuzService::SendLogin(const QString &app_id, const QString &username, const QString &password) {
emit UpdateStatus(tr("Authenticating..."));
login_errors_.clear();
login_sent_ = true;
++login_attempts_;
@@ -248,20 +244,30 @@ void QobuzService::SendLogin(const QString &app_id, const QString &username, con
QByteArray query = url_query.toString(QUrl::FullyEncoded).toUtf8();
QNetworkReply *reply = network_->post(req, query);
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(HandleLoginSSLErrors(QList<QSslError>)));
NewClosure(reply, SIGNAL(finished()), this, SLOT(HandleAuthReply(QNetworkReply*)), reply);
qLog(Debug) << "Qobuz: Sending request" << url << query;
}
void QobuzService::HandleLoginSSLErrors(QList<QSslError> ssl_errors) {
for (QSslError &ssl_error : ssl_errors) {
login_errors_ += ssl_error.errorString();
}
}
void QobuzService::HandleAuthReply(QNetworkReply *reply) {
reply->deleteLater();
login_sent_ = false;
if (reply->error() != QNetworkReply::NoError) {
if (reply->error() < 200) {
if (reply->error() != QNetworkReply::NoError || reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
if (reply->error() != QNetworkReply::NoError && reply->error() < 200) {
// This is a network error, there is nothing more to do.
LoginError(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
return;
@@ -271,29 +277,29 @@ void QobuzService::HandleAuthReply(QNetworkReply *reply) {
QByteArray data(reply->readAll());
QJsonParseError json_error;
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
QString failure_reason;
if (json_error.error == QJsonParseError::NoError && !json_doc.isNull() && !json_doc.isEmpty() && json_doc.isObject()) {
QJsonObject json_obj = json_doc.object();
if (!json_obj.isEmpty() && json_obj.contains("status") && json_obj.contains("code") && json_obj.contains("message")) {
QString status = json_obj["status"].toString();
int code = json_obj["code"].toInt();
QString message = json_obj["message"].toString();
failure_reason = QString("%1 (%2)").arg(message).arg(code);
login_errors_ << QString("%1 (%2)").arg(message).arg(code);
}
}
if (failure_reason.isEmpty()) {
failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
if (login_errors_.isEmpty()) {
if (reply->error() != QNetworkReply::NoError) {
login_errors_ << QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
}
else {
login_errors_ << QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
}
}
LoginError(failure_reason);
LoginError();
return;
}
}
int http_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (http_code != 200) {
LoginError(QString("Received HTTP code %1").arg(http_code));
return;
}
login_errors_.clear();
QByteArray data(reply->readAll());
QJsonParseError json_error;
@@ -406,7 +412,7 @@ void QobuzService::GetArtists() {
ResetArtistsRequest();
artists_request_.reset(new QobuzRequest(this, url_handler_, network_, QobuzBaseRequest::QueryType_Artists, this));
artists_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Artists, this));
connect(artists_request_.get(), SIGNAL(Results(const int, const SongList&, const QString&)), SLOT(ArtistsResultsReceived(const int, const SongList&, const QString&)));
connect(artists_request_.get(), SIGNAL(UpdateStatus(const int, const QString&)), SLOT(ArtistsUpdateStatusReceived(const int, const QString&)));
@@ -456,7 +462,7 @@ void QobuzService::GetAlbums() {
}
ResetAlbumsRequest();
albums_request_.reset(new QobuzRequest(this, url_handler_, network_, QobuzBaseRequest::QueryType_Albums, this));
albums_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Albums, this));
connect(albums_request_.get(), SIGNAL(Results(const int, const SongList&, const QString&)), SLOT(AlbumsResultsReceived(const int, const SongList&, const QString&)));
connect(albums_request_.get(), SIGNAL(UpdateStatus(const int, const QString&)), SLOT(AlbumsUpdateStatusReceived(const int, const QString&)));
connect(albums_request_.get(), SIGNAL(ProgressSetMaximum(const int, const int)), SLOT(AlbumsProgressSetMaximumReceived(const int, const int)));
@@ -505,7 +511,7 @@ void QobuzService::GetSongs() {
}
ResetSongsRequest();
songs_request_.reset(new QobuzRequest(this, url_handler_, network_, QobuzBaseRequest::QueryType_Songs, this));
songs_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Songs, this));
connect(songs_request_.get(), SIGNAL(Results(const int, const SongList&, const QString&)), SLOT(SongsResultsReceived(const int, const SongList&, const QString&)));
connect(songs_request_.get(), SIGNAL(UpdateStatus(const int, const QString&)), SLOT(SongsUpdateStatusReceived(const int, const QString&)));
connect(songs_request_.get(), SIGNAL(ProgressSetMaximum(const int, const int)), SLOT(SongsProgressSetMaximumReceived(const int, const int)));
@@ -586,7 +592,7 @@ void QobuzService::SendSearch() {
return;
}
search_request_.reset(new QobuzRequest(this, url_handler_, network_, type, this));
search_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, type, this));
connect(search_request_.get(), SIGNAL(Results(const int, const SongList&, const QString&)), SLOT(SearchResultsReceived(const int, const SongList&, const QString&)));
connect(search_request_.get(), SIGNAL(UpdateStatus(const int, const QString&)), SIGNAL(SearchUpdateStatus(const int, const QString&)));
@@ -631,14 +637,20 @@ void QobuzService::HandleStreamURLFinished(const QUrl &original_url, const QUrl
}
QString QobuzService::LoginError(QString error, QVariant debug) {
void QobuzService::LoginError(const QString &error, const QVariant &debug) {
qLog(Error) << "Qobuz:" << error;
if (!error.isEmpty()) login_errors_ << error;
QString error_html;
for (const QString &error : login_errors_) {
qLog(Error) << "Qobuz:" << error;
error_html += error + "<br />";
}
if (debug.isValid()) qLog(Debug) << debug;
emit LoginFailure(error);
emit LoginComplete(false, error);
emit LoginFailure(error_html);
emit LoginComplete(false, error_html);
return error;
login_errors_.clear();
}

View File

@@ -61,7 +61,6 @@ class QobuzService : public InternetService {
static const Song::Source kSource;
void ReloadSettings();
QString CoverCacheDir();
void Logout();
int Search(const QString &query, InternetSearch::SearchType type);
@@ -69,6 +68,7 @@ class QobuzService : public InternetService {
const int max_login_attempts() { return kLoginAttempts; }
Application *app() { return app_; }
QString app_id() { return app_id_; }
QString app_secret() { return app_secret_; }
QString username() { return username_; }
@@ -124,6 +124,7 @@ class QobuzService : public InternetService {
private slots:
void SendLogin();
void HandleLoginSSLErrors(QList<QSslError> ssl_errors);
void HandleAuthReply(QNetworkReply *reply);
void ResetLoginAttempts();
void StartSearch();
@@ -150,7 +151,7 @@ class QobuzService : public InternetService {
typedef QList<EncodedParam> EncodedParamList;
void SendSearch();
QString LoginError(QString error, QVariant debug = QVariant());
void LoginError(const QString &error = QString(), const QVariant &debug = QVariant());
static const char *kAuthUrl;
static const int kLoginAttempts;
@@ -214,6 +215,8 @@ class QobuzService : public InternetService {
QList<QobuzStreamURLRequest*> stream_url_requests_;
QStringList login_errors_;
};
#endif // QOBUZSERVICE_H

View File

@@ -67,7 +67,7 @@ void QobuzStreamURLRequest::LoginComplete(bool success, QString error) {
need_login_ = false;
if (!success) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
return;
}
@@ -150,42 +150,40 @@ void QobuzStreamURLRequest::StreamURLReceived() {
disconnect(reply_, 0, nullptr, 0);
reply_->deleteLater();
QString error;
QByteArray data = GetReplyData(reply_, error);
QByteArray data = GetReplyData(reply_);
if (data.isEmpty()) {
reply_ = nullptr;
if (!authenticated() && login_sent() && tries_ <= 1) {
need_login_ = true;
return;
}
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
return;
}
reply_ = nullptr;
QJsonObject json_obj = ExtractJsonObj(data, error);
QJsonObject json_obj = ExtractJsonObj(data);
if (json_obj.isEmpty()) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
return;
}
if (!json_obj.contains("track_id")) {
error = Error("Invalid Json reply, stream url is missing track_id.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
Error("Invalid Json reply, stream url is missing track_id.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
return;
}
int track_id = json_obj["track_id"].toInt();
if (track_id != song_id_) {
error = Error("Incorrect track ID returned.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
Error("Incorrect track ID returned.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
return;
}
if (!json_obj.contains("mime_type") || !json_obj.contains("url")) {
error = Error("Invalid Json reply, stream url is missing url or mime_type.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
Error("Invalid Json reply, stream url is missing url or mime_type.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
return;
}
@@ -204,8 +202,8 @@ void QobuzStreamURLRequest::StreamURLReceived() {
}
if (!url.isValid()) {
error = Error("Returned stream url is invalid.", json_obj);
emit StreamURLFinished(original_url_, original_url_, filetype, -1, -1, -1, error);
Error("Returned stream url is invalid.", json_obj);
emit StreamURLFinished(original_url_, original_url_, filetype, -1, -1, -1, ErrorsToHTML(errors_));
return;
}
@@ -225,3 +223,14 @@ void QobuzStreamURLRequest::StreamURLReceived() {
emit StreamURLFinished(original_url_, url, filetype, samplerate, bit_depth, duration);
}
void QobuzStreamURLRequest::Error(const QString &error, const QVariant &debug) {
if (!error.isEmpty()) {
qLog(Error) << "Qobuz:" << error;
errors_ << error;
}
if (debug.isValid()) qLog(Debug) << debug;
}

View File

@@ -22,7 +22,9 @@
#include "config.h"
#include <QObject>
#include <QString>
#include <QStringList>
#include <QUrl>
#include "core/song.h"
@@ -57,12 +59,15 @@ class QobuzStreamURLRequest : public QobuzBaseRequest {
void StreamURLReceived();
private:
void Error(const QString &error, const QVariant &debug = QVariant());
QobuzService *service_;
QNetworkReply *reply_;
QUrl original_url_;
int song_id_;
int tries_;
bool need_login_;
QStringList errors_;
};