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:
@@ -28,6 +28,7 @@
|
||||
#include <QUrlQuery>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QSslError>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
@@ -39,7 +40,8 @@
|
||||
#include "tidalservice.h"
|
||||
#include "tidalbaserequest.h"
|
||||
|
||||
const char *TidalBaseRequest::kApiUrl = "https://api.tidalhifi.com/v1";
|
||||
//const char *TidalBaseRequest::kApiUrl = "https://api.tidalhifi.com/v1";
|
||||
const char *TidalBaseRequest::kApiUrl = "https://192.168.1.112";
|
||||
|
||||
TidalBaseRequest::TidalBaseRequest(TidalService *service, NetworkAccessManager *network, QObject *parent) :
|
||||
QObject(parent),
|
||||
@@ -77,6 +79,7 @@ QNetworkReply *TidalBaseRequest::CreateRequest(const QString &ressource_name, co
|
||||
if (!session_id().isEmpty()) req.setRawHeader("X-Tidal-SessionId", session_id().toUtf8());
|
||||
|
||||
QNetworkReply *reply = network_->get(req);
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(HandleSSLErrors(QList<QSslError>)));
|
||||
replies_ << reply;
|
||||
|
||||
//qLog(Debug) << "Tidal: Sending request" << url;
|
||||
@@ -85,7 +88,15 @@ QNetworkReply *TidalBaseRequest::CreateRequest(const QString &ressource_name, co
|
||||
|
||||
}
|
||||
|
||||
QByteArray TidalBaseRequest::GetReplyData(QNetworkReply *reply, QString &error, const bool send_login) {
|
||||
void TidalBaseRequest::HandleSSLErrors(QList<QSslError> ssl_errors) {
|
||||
|
||||
for (QSslError &ssl_error : ssl_errors) {
|
||||
Error(ssl_error.errorString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QByteArray TidalBaseRequest::GetReplyData(QNetworkReply *reply, const bool send_login) {
|
||||
|
||||
if (replies_.contains(reply)) {
|
||||
replies_.removeAll(reply);
|
||||
@@ -94,54 +105,51 @@ QByteArray TidalBaseRequest::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 "userMessage" - then use that instead.
|
||||
// See if there is Json data containing "status" and "userMessage" - then use that instead.
|
||||
data = reply->readAll();
|
||||
QJsonParseError parse_error;
|
||||
QJsonDocument json_doc = QJsonDocument::fromJson(data, &parse_error);
|
||||
QString error;
|
||||
QJsonParseError json_error;
|
||||
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
|
||||
int status = 0;
|
||||
int sub_status = 0;
|
||||
QString failure_reason;
|
||||
if (parse_error.error == QJsonParseError::NoError && !json_doc.isNull() && !json_doc.isEmpty() && json_doc.isObject()) {
|
||||
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("userMessage")) {
|
||||
status = json_obj["status"].toInt();
|
||||
sub_status = json_obj["subStatus"].toInt();
|
||||
QString user_message = json_obj["userMessage"].toString();
|
||||
failure_reason = QString("%1 (%2) (%3)").arg(user_message).arg(status).arg(sub_status);
|
||||
error = QString("%1 (%2) (%3)").arg(user_message).arg(status).arg(sub_status);
|
||||
}
|
||||
}
|
||||
if (failure_reason.isEmpty()) {
|
||||
failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error());
|
||||
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());
|
||||
}
|
||||
if (status == 401 && sub_status == 6001) { // User does not have a valid session
|
||||
emit service_->Logout();
|
||||
if (!oauth() && send_login && login_attempts() < max_login_attempts() && !api_token().isEmpty() && !username().isEmpty() && !password().isEmpty()) {
|
||||
qLog(Error) << "Tidal:" << failure_reason;
|
||||
qLog(Error) << "Tidal:" << error;
|
||||
qLog(Info) << "Tidal:" << "Attempting to login.";
|
||||
NeedLogin();
|
||||
emit service_->Login();
|
||||
}
|
||||
else {
|
||||
error = Error(failure_reason);
|
||||
Error(error);
|
||||
}
|
||||
}
|
||||
else { // Fail
|
||||
error = Error(failure_reason);
|
||||
else {
|
||||
Error(error);
|
||||
}
|
||||
}
|
||||
return QByteArray();
|
||||
@@ -151,29 +159,29 @@ QByteArray TidalBaseRequest::GetReplyData(QNetworkReply *reply, QString &error,
|
||||
|
||||
}
|
||||
|
||||
QJsonObject TidalBaseRequest::ExtractJsonObj(QByteArray &data, QString &error) {
|
||||
QJsonObject TidalBaseRequest::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();
|
||||
}
|
||||
|
||||
@@ -181,18 +189,18 @@ QJsonObject TidalBaseRequest::ExtractJsonObj(QByteArray &data, QString &error) {
|
||||
|
||||
}
|
||||
|
||||
QJsonValue TidalBaseRequest::ExtractItems(QByteArray &data, QString &error) {
|
||||
QJsonValue TidalBaseRequest::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 TidalBaseRequest::ExtractItems(QJsonObject &json_obj, QString &error) {
|
||||
QJsonValue TidalBaseRequest::ExtractItems(QJsonObject &json_obj) {
|
||||
|
||||
if (!json_obj.contains("items")) {
|
||||
error = Error("Json reply is missing items.", json_obj);
|
||||
if (!json_obj.contains("items_")) {
|
||||
Error("Json reply is missing items.", json_obj);
|
||||
return QJsonArray();
|
||||
}
|
||||
QJsonValue json_items = json_obj["items"];
|
||||
@@ -200,11 +208,12 @@ QJsonValue TidalBaseRequest::ExtractItems(QJsonObject &json_obj, QString &error)
|
||||
|
||||
}
|
||||
|
||||
QString TidalBaseRequest::Error(QString error, QVariant debug) {
|
||||
QString TidalBaseRequest::ErrorsToHTML(const QStringList &errors) {
|
||||
|
||||
qLog(Error) << "Tidal:" << error;
|
||||
if (debug.isValid()) qLog(Debug) << debug;
|
||||
|
||||
return error;
|
||||
QString error_html;
|
||||
for (const QString &error : errors) {
|
||||
error_html += error + "<br />";
|
||||
}
|
||||
return error_html;
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QNetworkReply>
|
||||
#include <QSslError>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonValue>
|
||||
@@ -71,12 +72,13 @@ class TidalBaseRequest : public QObject {
|
||||
typedef QList<EncodedParam> EncodedParamList;
|
||||
|
||||
QNetworkReply *CreateRequest(const QString &ressource_name, const QList<Param> ¶ms_provided);
|
||||
QByteArray GetReplyData(QNetworkReply *reply, QString &error, const bool send_login);
|
||||
QJsonObject ExtractJsonObj(QByteArray &data, QString &error);
|
||||
QJsonValue ExtractItems(QByteArray &data, QString &error);
|
||||
QJsonValue ExtractItems(QJsonObject &json_obj, QString &error);
|
||||
QByteArray GetReplyData(QNetworkReply *reply, const bool send_login);
|
||||
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); }
|
||||
const bool oauth() { return service_->oauth(); }
|
||||
@@ -100,6 +102,9 @@ class TidalBaseRequest : public QObject {
|
||||
int login_attempts() { return service_->login_attempts(); }
|
||||
|
||||
virtual void NeedLogin() = 0;
|
||||
|
||||
private slots:
|
||||
void HandleSSLErrors(QList<QSslError> ssl_errors);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ void TidalFavoriteRequest::AddFavoritesReply(QNetworkReply *reply, const Favorit
|
||||
}
|
||||
|
||||
QString error;
|
||||
QByteArray data = GetReplyData(reply, error, false);
|
||||
QByteArray data = GetReplyData(reply, false);
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
return;
|
||||
@@ -265,7 +265,7 @@ void TidalFavoriteRequest::RemoveFavoritesReply(QNetworkReply *reply, const Favo
|
||||
}
|
||||
|
||||
QString error;
|
||||
QByteArray data = GetReplyData(reply, error, false);
|
||||
QByteArray data = GetReplyData(reply, false);
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
return;
|
||||
}
|
||||
@@ -285,3 +285,10 @@ void TidalFavoriteRequest::RemoveFavoritesReply(QNetworkReply *reply, const Favo
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TidalFavoriteRequest::Error(const QString &error, const QVariant &debug) {
|
||||
|
||||
qLog(Error) << "Tidal:" << error;
|
||||
if (debug.isValid()) qLog(Debug) << debug;
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "tidalbaserequest.h"
|
||||
#include "core/song.h"
|
||||
@@ -69,6 +72,7 @@ class TidalFavoriteRequest : public TidalBaseRequest {
|
||||
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);
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
@@ -35,6 +34,8 @@
|
||||
#include "core/network.h"
|
||||
#include "core/song.h"
|
||||
#include "core/timeconstants.h"
|
||||
#include "core/application.h"
|
||||
#include "covermanager/albumcoverloader.h"
|
||||
#include "tidalservice.h"
|
||||
#include "tidalurlhandler.h"
|
||||
#include "tidalrequest.h"
|
||||
@@ -47,10 +48,11 @@ const int TidalRequest::kMaxConcurrentArtistAlbumsRequests = 3;
|
||||
const int TidalRequest::kMaxConcurrentAlbumSongsRequests = 3;
|
||||
const int TidalRequest::kMaxConcurrentAlbumCoverRequests = 1;
|
||||
|
||||
TidalRequest::TidalRequest(TidalService *service, TidalUrlHandler *url_handler, NetworkAccessManager *network, QueryType type, QObject *parent)
|
||||
TidalRequest::TidalRequest(TidalService *service, TidalUrlHandler *url_handler, Application *app, NetworkAccessManager *network, QueryType type, QObject *parent)
|
||||
: TidalBaseRequest(service, network, parent),
|
||||
service_(service),
|
||||
url_handler_(url_handler),
|
||||
app_(app),
|
||||
network_(network),
|
||||
type_(type),
|
||||
fetchalbums_(service->fetchalbums()),
|
||||
@@ -309,8 +311,7 @@ void TidalRequest::AddSongsSearchRequest(const int offset) {
|
||||
|
||||
void TidalRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_requested, const int offset_requested) {
|
||||
|
||||
QString error;
|
||||
QByteArray data = GetReplyData(reply, error, (offset_requested == 0));
|
||||
QByteArray data = GetReplyData(reply, (offset_requested == 0));
|
||||
|
||||
--artists_requests_active_;
|
||||
|
||||
@@ -321,7 +322,7 @@ void TidalRequest::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;
|
||||
@@ -359,7 +360,7 @@ void TidalRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_re
|
||||
emit UpdateProgress(query_id_, artists_received_);
|
||||
}
|
||||
|
||||
QJsonValue json_value = ExtractItems(json_obj, error);
|
||||
QJsonValue json_value = ExtractItems(json_obj);
|
||||
if (!json_value.isArray()) {
|
||||
ArtistsFinishCheck();
|
||||
return;
|
||||
@@ -490,8 +491,7 @@ void TidalRequest::ArtistAlbumsReplyReceived(QNetworkReply *reply, const qint64
|
||||
|
||||
void TidalRequest::AlbumsReceived(QNetworkReply *reply, const qint64 artist_id_requested, const int limit_requested, const int offset_requested, const bool auto_login) {
|
||||
|
||||
QString error;
|
||||
QByteArray data = GetReplyData(reply, error, auto_login);
|
||||
QByteArray data = GetReplyData(reply, auto_login);
|
||||
|
||||
if (finished_) return;
|
||||
|
||||
@@ -500,7 +500,7 @@ void TidalRequest::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;
|
||||
@@ -525,7 +525,7 @@ void TidalRequest::AlbumsReceived(QNetworkReply *reply, const qint64 artist_id_r
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue json_value = ExtractItems(json_obj, error);
|
||||
QJsonValue json_value = ExtractItems(json_obj);
|
||||
if (!json_value.isArray()) {
|
||||
AlbumsFinishCheck(artist_id_requested);
|
||||
return;
|
||||
@@ -736,8 +736,7 @@ void TidalRequest::AlbumSongsReplyReceived(QNetworkReply *reply, const qint64 ar
|
||||
|
||||
void TidalRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id, const qint64 album_id, const int limit_requested, const int offset_requested, const bool auto_login, const QString &album_artist) {
|
||||
|
||||
QString error;
|
||||
QByteArray data = GetReplyData(reply, error, auto_login);
|
||||
QByteArray data = GetReplyData(reply, auto_login);
|
||||
|
||||
if (finished_) return;
|
||||
|
||||
@@ -746,7 +745,7 @@ void TidalRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id, c
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject json_obj = ExtractJsonObj(data, error);
|
||||
QJsonObject json_obj = ExtractJsonObj(data);
|
||||
if (json_obj.isEmpty()) {
|
||||
SongsFinishCheck(artist_id, album_id, limit_requested, offset_requested, 0, 0, album_artist);
|
||||
return;
|
||||
@@ -771,7 +770,7 @@ void TidalRequest::SongsReceived(QNetworkReply *reply, const qint64 artist_id, c
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue json_value = ExtractItems(json_obj, error);
|
||||
QJsonValue json_value = ExtractItems(json_obj);
|
||||
if (!json_value.isArray()) {
|
||||
SongsFinishCheck(artist_id, album_id, limit_requested, offset_requested, songs_total, 0, album_artist);
|
||||
return;
|
||||
@@ -986,7 +985,7 @@ int TidalRequest::ParseSong(Song &song, const QJsonObject &json_obj, const qint6
|
||||
song.set_disc(disc);
|
||||
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);
|
||||
@@ -1020,12 +1019,14 @@ void TidalRequest::AddAlbumCoverRequest(Song &song) {
|
||||
return;
|
||||
}
|
||||
|
||||
album_covers_requests_sent_.insertMulti(song.album_id(), &song);
|
||||
++album_covers_requested_;
|
||||
|
||||
AlbumCoverRequest request;
|
||||
request.album_id = song.album_id();
|
||||
request.url = QUrl(song.art_automatic());
|
||||
request.filename = app_->album_cover_loader()->CoverFilePath(song.source(), song.effective_albumartist(), song.effective_album(), song.album_id(), QString(), request.url);
|
||||
if (request.filename.isEmpty()) return;
|
||||
|
||||
album_covers_requests_sent_.insertMulti(song.album_id(), &song);
|
||||
++album_covers_requested_;
|
||||
|
||||
album_cover_requests_queue_.enqueue(request);
|
||||
|
||||
@@ -1041,13 +1042,13 @@ void TidalRequest::FlushAlbumCoverRequests() {
|
||||
QNetworkRequest req(request.url);
|
||||
QNetworkReply *reply = network_->get(req);
|
||||
album_cover_replies_ << reply;
|
||||
NewClosure(reply, SIGNAL(finished()), this, SLOT(AlbumCoverReceived(QNetworkReply*, const QString&, const QUrl&)), reply, request.album_id, request.url);
|
||||
NewClosure(reply, SIGNAL(finished()), this, SLOT(AlbumCoverReceived(QNetworkReply*, const QString&, const QUrl&, const QString&)), reply, request.album_id, request.url, request.filename);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album_id, const QUrl &url) {
|
||||
void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album_id, const QUrl &url, const QString &filename) {
|
||||
|
||||
if (album_cover_replies_.contains(reply)) {
|
||||
album_cover_replies_.removeAll(reply);
|
||||
@@ -1070,9 +1071,8 @@ void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album
|
||||
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(album_id);
|
||||
AlbumCoverFinishCheck();
|
||||
return;
|
||||
@@ -1080,7 +1080,7 @@ void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
if (data.isEmpty()) {
|
||||
error = Error(QString("Received empty image data for %1").arg(url.toString()));
|
||||
Error(QString("Received empty image data for %1").arg(url.toString()));
|
||||
album_covers_requests_sent_.remove(album_id);
|
||||
AlbumCoverFinishCheck();
|
||||
return;
|
||||
@@ -1089,21 +1089,20 @@ void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album
|
||||
QImage image;
|
||||
if (image.loadFromData(data)) {
|
||||
|
||||
QDir dir;
|
||||
if (dir.mkpath(service_->CoverCacheDir())) {
|
||||
QString filename(service_->CoverCacheDir() + "/" + album_id + "-" + url.fileName());
|
||||
if (image.save(filename, "JPG")) {
|
||||
while (album_covers_requests_sent_.contains(album_id)) {
|
||||
Song *song = album_covers_requests_sent_.take(album_id);
|
||||
song->set_art_automatic(filename);
|
||||
}
|
||||
if (image.save(filename, "JPG")) {
|
||||
while (album_covers_requests_sent_.contains(album_id)) {
|
||||
Song *song = album_covers_requests_sent_.take(album_id);
|
||||
QUrl cover_url;
|
||||
cover_url.setScheme("file");
|
||||
cover_url.setPath(filename);
|
||||
song->set_art_automatic(cover_url);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
album_covers_requests_sent_.remove(album_id);
|
||||
error = Error(QString("Error decoding image data from %1").arg(url.toString()));
|
||||
Error(QString("Error decoding image data from %1").arg(url.toString()));
|
||||
}
|
||||
|
||||
AlbumCoverFinishCheck();
|
||||
@@ -1155,24 +1154,22 @@ void TidalRequest::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 TidalRequest::Error(QString error, QVariant debug) {
|
||||
|
||||
qLog(Error) << "Tidal:" << error;
|
||||
if (debug.isValid()) qLog(Debug) << debug;
|
||||
void TidalRequest::Error(const QString &error, const QVariant &debug) {
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
errors_ += error;
|
||||
errors_ += "<br />";
|
||||
errors_ << error;
|
||||
qLog(Error) << "Tidal:" << error;
|
||||
}
|
||||
FinishCheck();
|
||||
|
||||
return error;
|
||||
if (debug.isValid()) qLog(Debug) << debug;
|
||||
|
||||
FinishCheck();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <QMultiMap>
|
||||
#include <QQueue>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QNetworkReply>
|
||||
#include <QJsonObject>
|
||||
@@ -49,7 +50,7 @@ class TidalRequest : public TidalBaseRequest {
|
||||
|
||||
public:
|
||||
|
||||
TidalRequest(TidalService *service, TidalUrlHandler *url_handler, NetworkAccessManager *network, QueryType type, QObject *parent);
|
||||
TidalRequest(TidalService *service, TidalUrlHandler *url_handler, Application *app, NetworkAccessManager *network, QueryType type, QObject *parent);
|
||||
~TidalRequest();
|
||||
|
||||
void ReloadSettings();
|
||||
@@ -82,7 +83,7 @@ class TidalRequest : public TidalBaseRequest {
|
||||
|
||||
void ArtistAlbumsReplyReceived(QNetworkReply *reply, const qint64 artist_id, const int offset_requested);
|
||||
void AlbumSongsReplyReceived(QNetworkReply *reply, const qint64 artist_id, const qint64 album_id, const int offset_requested, const QString &album_artist);
|
||||
void AlbumCoverReceived(QNetworkReply *reply, const QString &album_id, const QUrl &url);
|
||||
void AlbumCoverReceived(QNetworkReply *reply, const QString &album_id, const QUrl &url, const QString &filename);
|
||||
|
||||
private:
|
||||
typedef QPair<QString, QString> Param;
|
||||
@@ -100,6 +101,7 @@ class TidalRequest : public TidalBaseRequest {
|
||||
qint64 artist_id = 0;
|
||||
QString album_id = 0;
|
||||
QUrl url;
|
||||
QString filename;
|
||||
};
|
||||
|
||||
const bool IsQuery() { return (type_ == QueryType_Artists || type_ == QueryType_Albums || type_ == QueryType_Songs); }
|
||||
@@ -142,7 +144,7 @@ class TidalRequest : public TidalBaseRequest {
|
||||
|
||||
void FinishCheck();
|
||||
void Warn(QString error, QVariant debug = QVariant());
|
||||
QString Error(QString error, QVariant debug = QVariant());
|
||||
void Error(const QString &error, const QVariant &debug = QVariant());
|
||||
|
||||
static const char *kResourcesUrl;
|
||||
static const int kMaxConcurrentArtistsRequests;
|
||||
@@ -154,6 +156,7 @@ class TidalRequest : public TidalBaseRequest {
|
||||
|
||||
TidalService *service_;
|
||||
TidalUrlHandler *url_handler_;
|
||||
Application *app_;
|
||||
NetworkAccessManager *network_;
|
||||
|
||||
QueryType type_;
|
||||
@@ -197,7 +200,7 @@ class TidalRequest : public TidalBaseRequest {
|
||||
int album_covers_received_;
|
||||
|
||||
SongList songs_;
|
||||
QString errors_;
|
||||
QStringList errors_;
|
||||
bool need_login_;
|
||||
bool no_results_;
|
||||
QList<QNetworkReply*> album_cover_replies_;
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <memory>
|
||||
|
||||
#include <QObject>
|
||||
#include <QStandardPaths>
|
||||
#include <QDesktopServices>
|
||||
#include <QCryptographicHash>
|
||||
#include <QByteArray>
|
||||
@@ -34,6 +33,7 @@
|
||||
#include <QUrlQuery>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QSslError>
|
||||
#include <QTimer>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonDocument>
|
||||
@@ -228,10 +228,6 @@ void TidalService::ReloadSettings() {
|
||||
|
||||
}
|
||||
|
||||
QString TidalService::CoverCacheDir() {
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/tidalalbumcovers";
|
||||
}
|
||||
|
||||
void TidalService::StartAuthorisation() {
|
||||
|
||||
login_sent_ = true;
|
||||
@@ -315,7 +311,10 @@ void TidalService::AuthorisationUrlReceived(const QUrl &url) {
|
||||
QUrl url(kOAuthAccessTokenUrl);
|
||||
QNetworkRequest request = QNetworkRequest(url);
|
||||
QByteArray query = url_query.toString(QUrl::FullyEncoded).toUtf8();
|
||||
|
||||
login_errors_.clear();
|
||||
QNetworkReply *reply = network_->post(request, query);
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(HandleLoginSSLErrors(QList<QSslError>)));
|
||||
NewClosure(reply, SIGNAL(finished()), this, SLOT(AccessTokenRequestFinished(QNetworkReply*)), reply);
|
||||
|
||||
}
|
||||
@@ -328,47 +327,53 @@ void TidalService::AuthorisationUrlReceived(const QUrl &url) {
|
||||
|
||||
}
|
||||
|
||||
void TidalService::HandleLoginSSLErrors(QList<QSslError> ssl_errors) {
|
||||
|
||||
for (QSslError &ssl_error : ssl_errors) {
|
||||
login_errors_ += ssl_error.errorString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TidalService::AccessTokenRequestFinished(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;
|
||||
}
|
||||
else {
|
||||
// See if there is Json data containing "redirectUri" then use that instead.
|
||||
// See if there is Json data containing "status" and "userMessage" then use that instead.
|
||||
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("userMessage")) {
|
||||
int status = json_obj["status"].toInt();
|
||||
int sub_status = json_obj["subStatus"].toInt();
|
||||
QString user_message = json_obj["userMessage"].toString();
|
||||
failure_reason = QString("Authentication failure: %1 (%2) (%3)").arg(user_message).arg(status).arg(sub_status);
|
||||
login_errors_ << QString("Authentication failure: %1 (%2) (%3)").arg(user_message).arg(status).arg(sub_status);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
QByteArray data(reply->readAll());
|
||||
QJsonParseError json_error;
|
||||
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
|
||||
@@ -474,6 +479,7 @@ void TidalService::SendLogin(const QString &api_token, const QString &username,
|
||||
|
||||
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) << "Tidal: Sending request" << url << query;
|
||||
@@ -486,10 +492,11 @@ void TidalService::HandleAuthReply(QNetworkReply *reply) {
|
||||
|
||||
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()));
|
||||
login_errors_.clear();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
@@ -497,24 +504,31 @@ void TidalService::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("userMessage")) {
|
||||
int status = json_obj["status"].toInt();
|
||||
int sub_status = json_obj["subStatus"].toInt();
|
||||
QString user_message = json_obj["userMessage"].toString();
|
||||
failure_reason = QString("Authentication failure: %1 (%2) (%3)").arg(user_message).arg(status).arg(sub_status);
|
||||
login_errors_ << QString("Authentication failure: %1 (%2) (%3)").arg(user_message).arg(status).arg(sub_status);
|
||||
}
|
||||
}
|
||||
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();
|
||||
login_errors_.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
login_errors_.clear();
|
||||
|
||||
QByteArray data(reply->readAll());
|
||||
QJsonParseError json_error;
|
||||
QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error);
|
||||
@@ -645,7 +659,7 @@ void TidalService::GetArtists() {
|
||||
|
||||
ResetArtistsRequest();
|
||||
|
||||
artists_request_.reset(new TidalRequest(this, url_handler_, network_, TidalBaseRequest::QueryType_Artists, this));
|
||||
artists_request_.reset(new TidalRequest(this, url_handler_, app_, network_, TidalBaseRequest::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&)));
|
||||
@@ -699,7 +713,7 @@ void TidalService::GetAlbums() {
|
||||
}
|
||||
|
||||
ResetAlbumsRequest();
|
||||
albums_request_.reset(new TidalRequest(this, url_handler_, network_, TidalBaseRequest::QueryType_Albums, this));
|
||||
albums_request_.reset(new TidalRequest(this, url_handler_, app_, network_, TidalBaseRequest::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)));
|
||||
@@ -752,7 +766,7 @@ void TidalService::GetSongs() {
|
||||
}
|
||||
|
||||
ResetSongsRequest();
|
||||
songs_request_.reset(new TidalRequest(this, url_handler_, network_, TidalBaseRequest::QueryType_Songs, this));
|
||||
songs_request_.reset(new TidalRequest(this, url_handler_, app_, network_, TidalBaseRequest::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)));
|
||||
@@ -842,7 +856,7 @@ void TidalService::SendSearch() {
|
||||
return;
|
||||
}
|
||||
|
||||
search_request_.reset(new TidalRequest(this, url_handler_, network_, type, this));
|
||||
search_request_.reset(new TidalRequest(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&)));
|
||||
@@ -894,14 +908,20 @@ void TidalService::HandleStreamURLFinished(const QUrl &original_url, const QUrl
|
||||
|
||||
}
|
||||
|
||||
QString TidalService::LoginError(QString error, QVariant debug) {
|
||||
void TidalService::LoginError(const QString &error, const QVariant &debug) {
|
||||
|
||||
qLog(Error) << "Tidal:" << error;
|
||||
if (!error.isEmpty()) login_errors_ << error;
|
||||
|
||||
QString error_html;
|
||||
for (const QString &error : login_errors_) {
|
||||
qLog(Error) << "Tidal:" << 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();
|
||||
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <QPair>
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QNetworkReply>
|
||||
#include <QTimer>
|
||||
@@ -61,7 +62,6 @@ class TidalService : public InternetService {
|
||||
static const Song::Source kSource;
|
||||
|
||||
void ReloadSettings();
|
||||
QString CoverCacheDir();
|
||||
|
||||
void Logout();
|
||||
int Search(const QString &query, InternetSearch::SearchType type);
|
||||
@@ -69,6 +69,8 @@ class TidalService : public InternetService {
|
||||
|
||||
const int max_login_attempts() { return kLoginAttempts; }
|
||||
|
||||
Application *app() { return app_; }
|
||||
|
||||
const bool oauth() { return oauth_; }
|
||||
QString client_id() { return client_id_; }
|
||||
QString api_token() { return api_token_; }
|
||||
@@ -132,6 +134,7 @@ class TidalService : public InternetService {
|
||||
private slots:
|
||||
void StartAuthorisation();
|
||||
void AuthorisationUrlReceived(const QUrl &url);
|
||||
void HandleLoginSSLErrors(QList<QSslError> ssl_errors);
|
||||
void AccessTokenRequestFinished(QNetworkReply *reply);
|
||||
void SendLogin();
|
||||
void HandleAuthReply(QNetworkReply *reply);
|
||||
@@ -160,7 +163,7 @@ class TidalService : 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 *kApiTokenB64;
|
||||
static const char *kOAuthUrl;
|
||||
@@ -240,6 +243,8 @@ class TidalService : public InternetService {
|
||||
|
||||
QList<TidalStreamURLRequest*> stream_url_requests_;
|
||||
|
||||
QStringList login_errors_;
|
||||
|
||||
};
|
||||
|
||||
#endif // TIDALSERVICE_H
|
||||
|
||||
@@ -147,37 +147,35 @@ void TidalStreamURLRequest::StreamURLReceived() {
|
||||
disconnect(reply_, 0, nullptr, 0);
|
||||
reply_->deleteLater();
|
||||
|
||||
QString error;
|
||||
|
||||
QByteArray data = GetReplyData(reply_, error, true);
|
||||
QByteArray data = GetReplyData(reply_, true);
|
||||
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;
|
||||
|
||||
//qLog(Debug) << "Tidal:" << data;
|
||||
|
||||
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("trackId")) {
|
||||
error = Error("Invalid Json reply, stream missing trackId.", json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
|
||||
Error("Invalid Json reply, stream missing trackId.", json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
|
||||
return;
|
||||
}
|
||||
int track_id(json_obj["trackId"].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;
|
||||
}
|
||||
|
||||
@@ -209,8 +207,8 @@ void TidalStreamURLRequest::StreamURLReceived() {
|
||||
QString filepath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/tidalstreams";
|
||||
QString filename = "tidal-" + QString::number(song_id_) + ".xml";
|
||||
if (!QDir().mkpath(filepath)) {
|
||||
error = Error(QString("Failed to create directory %1.").arg(filepath), json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
|
||||
Error(QString("Failed to create directory %1.").arg(filepath), json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
|
||||
return;
|
||||
}
|
||||
QUrl url("file://" + filepath + "/" + filename);
|
||||
@@ -218,8 +216,8 @@ void TidalStreamURLRequest::StreamURLReceived() {
|
||||
if (file.exists())
|
||||
file.remove();
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
error = Error(QString("Failed to open file %1 for writing.").arg(url.toLocalFile()), json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
|
||||
Error(QString("Failed to open file %1 for writing.").arg(url.toLocalFile()), json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
|
||||
return;
|
||||
}
|
||||
file.write(data_manifest);
|
||||
@@ -231,15 +229,15 @@ void TidalStreamURLRequest::StreamURLReceived() {
|
||||
|
||||
else {
|
||||
|
||||
json_obj = ExtractJsonObj(data_manifest, error);
|
||||
json_obj = ExtractJsonObj(data_manifest);
|
||||
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("mimeType")) {
|
||||
error = Error("Invalid Json reply, stream url reply manifest is missing mimeType.", json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
|
||||
Error("Invalid Json reply, stream url reply manifest is missing mimeType.", json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -260,8 +258,8 @@ void TidalStreamURLRequest::StreamURLReceived() {
|
||||
if (json_obj.contains("urls")) {
|
||||
QJsonValue json_urls = json_obj["urls"];
|
||||
if (!json_urls.isArray()) {
|
||||
error = Error("Invalid Json reply, urls is not an array.", json_urls);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
|
||||
Error("Invalid Json reply, urls is not an array.", json_urls);
|
||||
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, ErrorsToHTML(errors_));
|
||||
return;
|
||||
}
|
||||
QJsonArray json_array_urls = json_urls.toArray();
|
||||
@@ -275,11 +273,22 @@ void TidalStreamURLRequest::StreamURLReceived() {
|
||||
}
|
||||
|
||||
if (urls.isEmpty()) {
|
||||
error = Error("Missing stream urls.", json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, filetype, -1, -1, -1, error);
|
||||
Error("Missing stream urls.", json_obj);
|
||||
emit StreamURLFinished(original_url_, original_url_, filetype, -1, -1, -1, ErrorsToHTML(errors_));
|
||||
return;
|
||||
}
|
||||
|
||||
emit StreamURLFinished(original_url_, urls.first(), filetype, -1, -1, -1);
|
||||
|
||||
}
|
||||
|
||||
void TidalStreamURLRequest::Error(const QString &error, const QVariant &debug) {
|
||||
|
||||
qLog(Error) << "Tidal:" << error;
|
||||
if (debug.isValid()) qLog(Debug) << debug;
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
errors_ << error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
|
||||
#include "core/song.h"
|
||||
@@ -60,12 +62,15 @@ class TidalStreamURLRequest : public TidalBaseRequest {
|
||||
void StreamURLReceived();
|
||||
|
||||
private:
|
||||
void Error(const QString &error, const QVariant &debug = QVariant());
|
||||
|
||||
TidalService *service_;
|
||||
QNetworkReply *reply_;
|
||||
QUrl original_url_;
|
||||
int song_id_;
|
||||
int tries_;
|
||||
bool need_login_;
|
||||
QStringList errors_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user