Refactor Tidal, Spotify, Qobuz, Subsonic and cover providers
Use common HTTP, Json and OAuthenticator class
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Strawberry Music Player
|
||||
* Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
|
||||
* Copyright 2018-2025, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QObject>
|
||||
#include <QList>
|
||||
#include <QByteArray>
|
||||
#include <QByteArrayList>
|
||||
@@ -35,6 +34,7 @@
|
||||
#include <QJsonArray>
|
||||
#include <QJsonValue>
|
||||
#include <QTimer>
|
||||
#include <QScopeGuard>
|
||||
|
||||
#include "includes/shared_ptr.h"
|
||||
#include "core/logging.h"
|
||||
@@ -107,24 +107,6 @@ TidalRequest::TidalRequest(TidalService *service, TidalUrlHandler *url_handler,
|
||||
|
||||
}
|
||||
|
||||
TidalRequest::~TidalRequest() {
|
||||
|
||||
while (!replies_.isEmpty()) {
|
||||
QNetworkReply *reply = replies_.takeFirst();
|
||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||
if (reply->isRunning()) reply->abort();
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
while (!album_cover_replies_.isEmpty()) {
|
||||
QNetworkReply *reply = album_cover_replies_.takeFirst();
|
||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||
if (reply->isRunning()) reply->abort();
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void TidalRequest::Process() {
|
||||
|
||||
switch (query_type_) {
|
||||
@@ -227,7 +209,7 @@ void TidalRequest::FlushArtistsRequests() {
|
||||
|
||||
while (!artists_requests_queue_.isEmpty() && artists_requests_active_ < kMaxConcurrentArtistsRequests) {
|
||||
|
||||
Request request = artists_requests_queue_.dequeue();
|
||||
const Request request = artists_requests_queue_.dequeue();
|
||||
|
||||
ParamList parameters;
|
||||
if (query_type_ == Type::SearchArtists) parameters << Param(u"query"_s, search_text_);
|
||||
@@ -241,7 +223,6 @@ void TidalRequest::FlushArtistsRequests() {
|
||||
reply = CreateRequest(u"search/artists"_s, parameters);
|
||||
}
|
||||
if (!reply) continue;
|
||||
replies_ << reply;
|
||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, request]() { ArtistsReplyReceived(reply, request.limit, request.offset); });
|
||||
|
||||
++artists_requests_active_;
|
||||
@@ -275,7 +256,7 @@ void TidalRequest::FlushAlbumsRequests() {
|
||||
|
||||
while (!albums_requests_queue_.isEmpty() && albums_requests_active_ < kMaxConcurrentAlbumsRequests) {
|
||||
|
||||
Request request = albums_requests_queue_.dequeue();
|
||||
const Request request = albums_requests_queue_.dequeue();
|
||||
|
||||
ParamList parameters;
|
||||
if (query_type_ == Type::SearchAlbums) parameters << Param(u"query"_s, search_text_);
|
||||
@@ -289,7 +270,6 @@ void TidalRequest::FlushAlbumsRequests() {
|
||||
reply = CreateRequest(u"search/albums"_s, parameters);
|
||||
}
|
||||
if (!reply) continue;
|
||||
replies_ << reply;
|
||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, request]() { AlbumsReplyReceived(reply, request.limit, request.offset); });
|
||||
|
||||
++albums_requests_active_;
|
||||
@@ -323,7 +303,7 @@ void TidalRequest::FlushSongsRequests() {
|
||||
|
||||
while (!songs_requests_queue_.isEmpty() && songs_requests_active_ < kMaxConcurrentSongsRequests) {
|
||||
|
||||
Request request = songs_requests_queue_.dequeue();
|
||||
const Request request = songs_requests_queue_.dequeue();
|
||||
|
||||
ParamList parameters;
|
||||
if (query_type_ == Type::SearchSongs) parameters << Param(u"query"_s, search_text_);
|
||||
@@ -337,7 +317,6 @@ void TidalRequest::FlushSongsRequests() {
|
||||
reply = CreateRequest(u"search/tracks"_s, parameters);
|
||||
}
|
||||
if (!reply) continue;
|
||||
replies_ << reply;
|
||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, request]() { SongsReplyReceived(reply, request.limit, request.offset); });
|
||||
|
||||
++songs_requests_active_;
|
||||
@@ -395,48 +374,44 @@ void TidalRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_re
|
||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||
reply->deleteLater();
|
||||
|
||||
QByteArray data = GetReplyData(reply);
|
||||
const JsonObjectResult json_object_result = ParseJsonObject(reply);
|
||||
|
||||
--artists_requests_active_;
|
||||
++artists_requests_received_;
|
||||
|
||||
if (finished_) return;
|
||||
|
||||
if (data.isEmpty()) {
|
||||
ArtistsFinishCheck();
|
||||
return;
|
||||
}
|
||||
int offset = 0;
|
||||
int artists_received = 0;
|
||||
const QScopeGuard finish_check = qScopeGuard([this, limit_requested, &offset, &artists_received]() { ArtistsFinishCheck(limit_requested, offset, artists_received); });
|
||||
|
||||
QJsonObject json_obj = ExtractJsonObj(data);
|
||||
if (json_obj.isEmpty()) {
|
||||
ArtistsFinishCheck();
|
||||
if (!json_object_result.success()) {
|
||||
Error(json_object_result.error_message);
|
||||
return;
|
||||
}
|
||||
const QJsonObject &json_object = json_object_result.json_object;
|
||||
|
||||
if (!json_obj.contains("limit"_L1) ||
|
||||
!json_obj.contains("offset"_L1) ||
|
||||
!json_obj.contains("totalNumberOfItems"_L1) ||
|
||||
!json_obj.contains("items"_L1)) {
|
||||
Error(u"Json object missing values."_s, json_obj);
|
||||
ArtistsFinishCheck();
|
||||
if (!json_object.contains("limit"_L1) ||
|
||||
!json_object.contains("offset"_L1) ||
|
||||
!json_object.contains("totalNumberOfItems"_L1) ||
|
||||
!json_object.contains("items"_L1)) {
|
||||
Error(u"Json object missing values."_s, json_object);
|
||||
return;
|
||||
}
|
||||
//int limit = json_obj["limit"].toInt();
|
||||
int offset = json_obj["offset"_L1].toInt();
|
||||
int artists_total = json_obj["totalNumberOfItems"_L1].toInt();
|
||||
//int limit = json_object["limit"].toInt();
|
||||
offset = json_object["offset"_L1].toInt();
|
||||
const int artists_total = json_object["totalNumberOfItems"_L1].toInt();
|
||||
|
||||
if (offset_requested == 0) {
|
||||
artists_total_ = artists_total;
|
||||
}
|
||||
else if (artists_total != artists_total_) {
|
||||
Error(QStringLiteral("totalNumberOfItems returned does not match previous totalNumberOfItems! %1 != %2").arg(artists_total).arg(artists_total_));
|
||||
ArtistsFinishCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset != offset_requested) {
|
||||
Error(QStringLiteral("Offset returned does not match offset requested! %1 != %2").arg(offset).arg(offset_requested));
|
||||
ArtistsFinishCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -444,19 +419,17 @@ void TidalRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_re
|
||||
Q_EMIT UpdateProgress(query_id_, GetProgress(artists_received_, artists_total_));
|
||||
}
|
||||
|
||||
QJsonValue value_items = ExtractItems(json_obj);
|
||||
if (!value_items.isArray()) {
|
||||
ArtistsFinishCheck();
|
||||
const JsonArrayResult json_array_result = GetJsonArray(json_object, u"items"_s);
|
||||
if (!json_array_result.success()) {
|
||||
Error(json_array_result.error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonArray array_items = value_items.toArray();
|
||||
const QJsonArray &array_items = json_array_result.json_array;
|
||||
if (array_items.isEmpty()) { // Empty array means no results
|
||||
ArtistsFinishCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
int artists_received = 0;
|
||||
for (const QJsonValue &value_item : array_items) {
|
||||
|
||||
++artists_received;
|
||||
@@ -465,30 +438,30 @@ void TidalRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_re
|
||||
Error(u"Invalid Json reply, item in array is not a object."_s);
|
||||
continue;
|
||||
}
|
||||
QJsonObject obj_item = value_item.toObject();
|
||||
QJsonObject object_item = value_item.toObject();
|
||||
|
||||
if (obj_item.contains("item"_L1)) {
|
||||
QJsonValue json_item = obj_item["item"_L1];
|
||||
if (object_item.contains("item"_L1)) {
|
||||
const QJsonValue json_item = object_item["item"_L1];
|
||||
if (!json_item.isObject()) {
|
||||
Error(u"Invalid Json reply, item in array is not a object."_s, json_item);
|
||||
continue;
|
||||
}
|
||||
obj_item = json_item.toObject();
|
||||
object_item = json_item.toObject();
|
||||
}
|
||||
|
||||
if (!obj_item.contains("id"_L1) || !obj_item.contains("name"_L1)) {
|
||||
Error(u"Invalid Json reply, item missing id or album."_s, obj_item);
|
||||
if (!object_item.contains("id"_L1) || !object_item.contains("name"_L1)) {
|
||||
Error(u"Invalid Json reply, item missing id or album."_s, object_item);
|
||||
continue;
|
||||
}
|
||||
|
||||
Artist artist;
|
||||
if (obj_item["id"_L1].isString()) {
|
||||
artist.artist_id = obj_item["id"_L1].toString();
|
||||
if (object_item["id"_L1].isString()) {
|
||||
artist.artist_id = object_item["id"_L1].toString();
|
||||
}
|
||||
else {
|
||||
artist.artist_id = QString::number(obj_item["id"_L1].toInt());
|
||||
artist.artist_id = QString::number(object_item["id"_L1].toInt());
|
||||
}
|
||||
artist.artist = obj_item["name"_L1].toString();
|
||||
artist.artist = object_item["name"_L1].toString();
|
||||
|
||||
if (artist_albums_requests_pending_.contains(artist.artist_id)) continue;
|
||||
|
||||
@@ -501,8 +474,6 @@ void TidalRequest::ArtistsReplyReceived(QNetworkReply *reply, const int limit_re
|
||||
|
||||
if (offset_requested != 0) Q_EMIT UpdateProgress(query_id_, GetProgress(artists_received_, artists_total_));
|
||||
|
||||
ArtistsFinishCheck(limit_requested, offset, artists_received);
|
||||
|
||||
}
|
||||
|
||||
void TidalRequest::ArtistsFinishCheck(const int limit, const int offset, const int artists_received) {
|
||||
@@ -569,7 +540,6 @@ void TidalRequest::FlushArtistAlbumsRequests() {
|
||||
if (request.offset > 0) parameters << Param(u"offset"_s, QString::number(request.offset));
|
||||
QNetworkReply *reply = CreateRequest(QStringLiteral("artists/%1/albums").arg(request.artist.artist_id), parameters);
|
||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, request]() { ArtistAlbumsReplyReceived(reply, request.artist, request.offset); });
|
||||
replies_ << reply;
|
||||
|
||||
++artist_albums_requests_active_;
|
||||
|
||||
@@ -593,52 +563,54 @@ void TidalRequest::AlbumsReceived(QNetworkReply *reply, const Artist &artist_req
|
||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||
reply->deleteLater();
|
||||
|
||||
QByteArray data = GetReplyData(reply);
|
||||
const JsonObjectResult json_object_result = ParseJsonObject(reply);
|
||||
|
||||
if (finished_) return;
|
||||
|
||||
if (data.isEmpty()) {
|
||||
AlbumsFinishCheck(artist_requested);
|
||||
int offset = 0;
|
||||
int albums_total = 0;
|
||||
int albums_received = 0;
|
||||
const QScopeGuard finish_check = qScopeGuard([this, artist_requested, limit_requested, &offset, &albums_total, &albums_received]() { AlbumsFinishCheck(artist_requested, limit_requested, offset, albums_total, albums_received); });
|
||||
|
||||
if (!json_object_result.success()) {
|
||||
Error(json_object_result.error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject json_obj = ExtractJsonObj(data);
|
||||
if (json_obj.isEmpty()) {
|
||||
AlbumsFinishCheck(artist_requested);
|
||||
const QJsonObject &json_object = json_object_result.json_object;
|
||||
if (json_object.isEmpty()) {
|
||||
Error(json_object_result.error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json_obj.contains("limit"_L1) ||
|
||||
!json_obj.contains("offset"_L1) ||
|
||||
!json_obj.contains("totalNumberOfItems"_L1) ||
|
||||
!json_obj.contains("items"_L1)) {
|
||||
Error(u"Json object missing values."_s, json_obj);
|
||||
AlbumsFinishCheck(artist_requested);
|
||||
if (!json_object.contains("limit"_L1) ||
|
||||
!json_object.contains("offset"_L1) ||
|
||||
!json_object.contains("totalNumberOfItems"_L1) ||
|
||||
!json_object.contains("items"_L1)) {
|
||||
Error(u"Json object missing values."_s, json_object);
|
||||
return;
|
||||
}
|
||||
|
||||
//int limit = json_obj["limit"].toInt();
|
||||
int offset = json_obj["offset"_L1].toInt();
|
||||
int albums_total = json_obj["totalNumberOfItems"_L1].toInt();
|
||||
offset = json_object["offset"_L1].toInt();
|
||||
albums_total = json_object["totalNumberOfItems"_L1].toInt();
|
||||
|
||||
if (offset != offset_requested) {
|
||||
Error(QStringLiteral("Offset returned does not match offset requested! %1 != %2").arg(offset).arg(offset_requested));
|
||||
AlbumsFinishCheck(artist_requested);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue value_items = ExtractItems(json_obj);
|
||||
if (!value_items.isArray()) {
|
||||
AlbumsFinishCheck(artist_requested);
|
||||
const JsonArrayResult json_array_result = GetJsonArray(json_object, u"items"_s);
|
||||
if (!json_array_result.success()) {
|
||||
Error(json_array_result.error_message);
|
||||
return;
|
||||
}
|
||||
const QJsonArray array_items = value_items.toArray();
|
||||
|
||||
const QJsonArray &array_items = json_array_result.json_array;
|
||||
if (array_items.isEmpty()) {
|
||||
AlbumsFinishCheck(artist_requested);
|
||||
return;
|
||||
}
|
||||
|
||||
int albums_received = 0;
|
||||
for (const QJsonValue &value_item : array_items) {
|
||||
|
||||
++albums_received;
|
||||
@@ -647,79 +619,79 @@ void TidalRequest::AlbumsReceived(QNetworkReply *reply, const Artist &artist_req
|
||||
Error(u"Invalid Json reply, item in array is not a object."_s);
|
||||
continue;
|
||||
}
|
||||
QJsonObject obj_item = value_item.toObject();
|
||||
QJsonObject object_item = value_item.toObject();
|
||||
|
||||
if (obj_item.contains("item"_L1)) {
|
||||
QJsonValue json_item = obj_item["item"_L1];
|
||||
if (object_item.contains("item"_L1)) {
|
||||
const QJsonValue json_item = object_item["item"_L1];
|
||||
if (!json_item.isObject()) {
|
||||
Error(u"Invalid Json reply, item in array is not a object."_s, json_item);
|
||||
continue;
|
||||
}
|
||||
obj_item = json_item.toObject();
|
||||
object_item = json_item.toObject();
|
||||
}
|
||||
|
||||
Album album;
|
||||
if (obj_item.contains("type"_L1)) { // This was an albums request or search
|
||||
if (!obj_item.contains("id"_L1) || !obj_item.contains("title"_L1)) {
|
||||
Error(u"Invalid Json reply, item is missing ID or title."_s, obj_item);
|
||||
if (object_item.contains("type"_L1)) { // This was an albums request or search
|
||||
if (!object_item.contains("id"_L1) || !object_item.contains("title"_L1)) {
|
||||
Error(u"Invalid Json reply, item is missing ID or title."_s, object_item);
|
||||
continue;
|
||||
}
|
||||
if (obj_item["id"_L1].isString()) {
|
||||
album.album_id = obj_item["id"_L1].toString();
|
||||
if (object_item["id"_L1].isString()) {
|
||||
album.album_id = object_item["id"_L1].toString();
|
||||
}
|
||||
else {
|
||||
album.album_id = QString::number(obj_item["id"_L1].toInt());
|
||||
album.album_id = QString::number(object_item["id"_L1].toInt());
|
||||
}
|
||||
album.album = obj_item["title"_L1].toString();
|
||||
if (service_->album_explicit() && obj_item.contains("explicit"_L1)) {
|
||||
album.album_explicit = obj_item["explicit"_L1].toVariant().toBool();
|
||||
album.album = object_item["title"_L1].toString();
|
||||
if (service_->album_explicit() && object_item.contains("explicit"_L1)) {
|
||||
album.album_explicit = object_item["explicit"_L1].toVariant().toBool();
|
||||
if (album.album_explicit && !album.album.isEmpty()) {
|
||||
album.album.append(" (Explicit)"_L1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (obj_item.contains("album"_L1)) { // This was a tracks request or search
|
||||
QJsonValue value_album = obj_item["album"_L1];
|
||||
else if (object_item.contains("album"_L1)) { // This was a tracks request or search
|
||||
const QJsonValue value_album = object_item["album"_L1];
|
||||
if (!value_album.isObject()) {
|
||||
Error(u"Invalid Json reply, item album is not a object."_s, value_album);
|
||||
continue;
|
||||
}
|
||||
QJsonObject obj_album = value_album.toObject();
|
||||
if (!obj_album.contains("id"_L1) || !obj_album.contains("title"_L1)) {
|
||||
Error(u"Invalid Json reply, item album is missing ID or title."_s, obj_album);
|
||||
const QJsonObject object_album = value_album.toObject();
|
||||
if (!object_album.contains("id"_L1) || !object_album.contains("title"_L1)) {
|
||||
Error(u"Invalid Json reply, item album is missing ID or title."_s, object_album);
|
||||
continue;
|
||||
}
|
||||
if (obj_album["id"_L1].isString()) {
|
||||
album.album_id = obj_album["id"_L1].toString();
|
||||
if (object_album["id"_L1].isString()) {
|
||||
album.album_id = object_album["id"_L1].toString();
|
||||
}
|
||||
else {
|
||||
album.album_id = QString::number(obj_album["id"_L1].toInt());
|
||||
album.album_id = QString::number(object_album["id"_L1].toInt());
|
||||
}
|
||||
album.album = obj_album["title"_L1].toString();
|
||||
if (service_->album_explicit() && obj_album.contains("explicit"_L1)) {
|
||||
album.album_explicit = obj_album["explicit"_L1].toVariant().toBool();
|
||||
album.album = object_album["title"_L1].toString();
|
||||
if (service_->album_explicit() && object_album.contains("explicit"_L1)) {
|
||||
album.album_explicit = object_album["explicit"_L1].toVariant().toBool();
|
||||
if (album.album_explicit && !album.album.isEmpty()) {
|
||||
album.album.append(" (Explicit)"_L1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
Error(u"Invalid Json reply, item missing type or album."_s, obj_item);
|
||||
Error(u"Invalid Json reply, item missing type or album."_s, object_item);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (album_songs_requests_pending_.contains(album.album_id)) continue;
|
||||
|
||||
if (!obj_item.contains("artist"_L1) || !obj_item.contains("title"_L1) || !obj_item.contains("audioQuality"_L1)) {
|
||||
Error(u"Invalid Json reply, item missing artist, title or audioQuality."_s, obj_item);
|
||||
if (!object_item.contains("artist"_L1) || !object_item.contains("title"_L1) || !object_item.contains("audioQuality"_L1)) {
|
||||
Error(u"Invalid Json reply, item missing artist, title or audioQuality."_s, object_item);
|
||||
continue;
|
||||
}
|
||||
QJsonValue value_artist = obj_item["artist"_L1];
|
||||
const QJsonValue value_artist = object_item["artist"_L1];
|
||||
if (!value_artist.isObject()) {
|
||||
Error(u"Invalid Json reply, item artist is not a object."_s, value_artist);
|
||||
continue;
|
||||
}
|
||||
QJsonObject obj_artist = value_artist.toObject();
|
||||
const QJsonObject obj_artist = value_artist.toObject();
|
||||
if (!obj_artist.contains("id"_L1) || !obj_artist.contains("name"_L1)) {
|
||||
Error(u"Invalid Json reply, item artist missing id or name."_s, obj_artist);
|
||||
continue;
|
||||
@@ -751,8 +723,6 @@ void TidalRequest::AlbumsReceived(QNetworkReply *reply, const Artist &artist_req
|
||||
Q_EMIT UpdateProgress(query_id_, GetProgress(albums_received_, albums_total_));
|
||||
}
|
||||
|
||||
AlbumsFinishCheck(artist_requested, limit_requested, offset, albums_total, albums_received);
|
||||
|
||||
}
|
||||
|
||||
void TidalRequest::AlbumsFinishCheck(const Artist &artist, const int limit, const int offset, const int albums_total, const int albums_received) {
|
||||
@@ -843,7 +813,6 @@ void TidalRequest::FlushAlbumSongsRequests() {
|
||||
ParamList parameters;
|
||||
if (request.offset > 0) parameters << Param(u"offset"_s, QString::number(request.offset));
|
||||
QNetworkReply *reply = CreateRequest(QStringLiteral("albums/%1/tracks").arg(request.album.album_id), parameters);
|
||||
replies_ << reply;
|
||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, request]() { AlbumSongsReplyReceived(reply, request.artist, request.album, request.offset); });
|
||||
|
||||
++album_songs_requests_active_;
|
||||
@@ -870,76 +839,75 @@ void TidalRequest::SongsReceived(QNetworkReply *reply, const Artist &artist, con
|
||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||
reply->deleteLater();
|
||||
|
||||
QByteArray data = GetReplyData(reply);
|
||||
const JsonObjectResult json_object_result = ParseJsonObject(reply);
|
||||
|
||||
if (finished_) return;
|
||||
|
||||
if (data.isEmpty()) {
|
||||
SongsFinishCheck(artist, album, limit_requested, offset_requested);
|
||||
int songs_total = 0;
|
||||
int songs_received = 0;
|
||||
const QScopeGuard finish_check = qScopeGuard([this, artist, album, limit_requested, offset_requested, &songs_total, &songs_received]() { SongsFinishCheck(artist, album, limit_requested, offset_requested, songs_total, songs_received); });
|
||||
|
||||
if (!json_object_result.success()) {
|
||||
Error(json_object_result.error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject json_obj = ExtractJsonObj(data);
|
||||
if (json_obj.isEmpty()) {
|
||||
SongsFinishCheck(artist, album, limit_requested, offset_requested);
|
||||
const QJsonObject &json_object = json_object_result.json_object;
|
||||
if (json_object.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!json_obj.contains("limit"_L1) ||
|
||||
!json_obj.contains("offset"_L1) ||
|
||||
!json_obj.contains("totalNumberOfItems"_L1) ||
|
||||
!json_obj.contains("items"_L1)) {
|
||||
Error(u"Json object missing values."_s, json_obj);
|
||||
SongsFinishCheck(artist, album, limit_requested, offset_requested);
|
||||
if (!json_object.contains("limit"_L1) ||
|
||||
!json_object.contains("offset"_L1) ||
|
||||
!json_object.contains("totalNumberOfItems"_L1) ||
|
||||
!json_object.contains("items"_L1)) {
|
||||
Error(u"Json object missing values."_s, json_object);
|
||||
return;
|
||||
}
|
||||
|
||||
//int limit = json_obj["limit"].toInt();
|
||||
int offset = json_obj["offset"_L1].toInt();
|
||||
int songs_total = json_obj["totalNumberOfItems"_L1].toInt();
|
||||
const int offset = json_object["offset"_L1].toInt();
|
||||
songs_total = json_object["totalNumberOfItems"_L1].toInt();
|
||||
|
||||
if (offset != offset_requested) {
|
||||
Error(QStringLiteral("Offset returned does not match offset requested! %1 != %2").arg(offset).arg(offset_requested));
|
||||
SongsFinishCheck(artist, album, limit_requested, offset_requested, songs_total, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue json_value = ExtractItems(json_obj);
|
||||
if (!json_value.isArray()) {
|
||||
SongsFinishCheck(artist, album, limit_requested, offset_requested, songs_total, 0);
|
||||
const JsonArrayResult json_array_result = GetJsonArray(json_object, u"items"_s);
|
||||
if (!json_array_result.success()) {
|
||||
Error(json_array_result.error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
const QJsonArray array_items = json_value.toArray();
|
||||
const QJsonArray &array_items = json_array_result.json_array;
|
||||
if (array_items.isEmpty()) {
|
||||
SongsFinishCheck(artist, album, limit_requested, offset_requested, songs_total, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
bool compilation = false;
|
||||
bool multidisc = false;
|
||||
SongList songs;
|
||||
int songs_received = 0;
|
||||
for (const QJsonValue &value_item : array_items) {
|
||||
|
||||
if (!value_item.isObject()) {
|
||||
Error(u"Invalid Json reply, track is not a object."_s);
|
||||
continue;
|
||||
}
|
||||
QJsonObject obj_item = value_item.toObject();
|
||||
QJsonObject object_item = value_item.toObject();
|
||||
|
||||
if (obj_item.contains("item"_L1)) {
|
||||
QJsonValue item = obj_item["item"_L1];
|
||||
if (object_item.contains("item"_L1)) {
|
||||
const QJsonValue item = object_item["item"_L1];
|
||||
if (!item.isObject()) {
|
||||
Error(u"Invalid Json reply, item is not a object."_s, item);
|
||||
continue;
|
||||
}
|
||||
obj_item = item.toObject();
|
||||
object_item = item.toObject();
|
||||
}
|
||||
|
||||
++songs_received;
|
||||
Song song(Song::Source::Tidal);
|
||||
ParseSong(song, obj_item, artist, album);
|
||||
ParseSong(song, object_item, artist, album);
|
||||
if (!song.is_valid()) continue;
|
||||
if (song.disc() >= 2) multidisc = true;
|
||||
if (song.is_compilation()) compilation = true;
|
||||
@@ -957,8 +925,6 @@ void TidalRequest::SongsReceived(QNetworkReply *reply, const Artist &artist, con
|
||||
Q_EMIT UpdateProgress(query_id_, GetProgress(songs_received_, songs_total_));
|
||||
}
|
||||
|
||||
SongsFinishCheck(artist, album, limit_requested, offset_requested, songs_total, songs_received);
|
||||
|
||||
}
|
||||
|
||||
void TidalRequest::SongsFinishCheck(const Artist &artist, const Album &album, const int limit, const int offset, const int songs_total, const int songs_received) {
|
||||
@@ -1017,10 +983,10 @@ void TidalRequest::ParseSong(Song &song, const QJsonObject &json_obj, const Arti
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue value_artist = json_obj["artist"_L1];
|
||||
QJsonValue value_album = json_obj["album"_L1];
|
||||
QJsonValue json_duration = json_obj["duration"_L1];
|
||||
//QJsonArray array_artists = json_obj["artists"].toArray();
|
||||
const QJsonValue value_artist = json_obj["artist"_L1];
|
||||
const QJsonValue value_album = json_obj["album"_L1];
|
||||
const QJsonValue json_duration = json_obj["duration"_L1];
|
||||
//const QJsonArray array_artists = json_obj["artists"].toArray();
|
||||
|
||||
QString song_id;
|
||||
if (json_obj["id"_L1].isString()) {
|
||||
@@ -1031,52 +997,52 @@ void TidalRequest::ParseSong(Song &song, const QJsonObject &json_obj, const Arti
|
||||
}
|
||||
|
||||
QString title = json_obj["title"_L1].toString();
|
||||
//QString urlstr = json_obj["url"].toString();
|
||||
int track = json_obj["trackNumber"_L1].toInt();
|
||||
int disc = json_obj["volumeNumber"_L1].toInt();
|
||||
bool allow_streaming = json_obj["allowStreaming"_L1].toBool();
|
||||
bool stream_ready = json_obj["streamReady"_L1].toBool();
|
||||
QString copyright = json_obj["copyright"_L1].toString();
|
||||
//const QString urlstr = json_obj["url"].toString();
|
||||
const int track = json_obj["trackNumber"_L1].toInt();
|
||||
const int disc = json_obj["volumeNumber"_L1].toInt();
|
||||
const bool allow_streaming = json_obj["allowStreaming"_L1].toBool();
|
||||
const bool stream_ready = json_obj["streamReady"_L1].toBool();
|
||||
const QString copyright = json_obj["copyright"_L1].toString();
|
||||
|
||||
if (!value_artist.isObject()) {
|
||||
Error(u"Invalid Json reply, track artist is not a object."_s, value_artist);
|
||||
return;
|
||||
}
|
||||
QJsonObject obj_artist = value_artist.toObject();
|
||||
if (!obj_artist.contains("id"_L1) || !obj_artist.contains("name"_L1)) {
|
||||
Error(u"Invalid Json reply, track artist is missing id or name."_s, obj_artist);
|
||||
const QJsonObject object_artist = value_artist.toObject();
|
||||
if (!object_artist.contains("id"_L1) || !object_artist.contains("name"_L1)) {
|
||||
Error(u"Invalid Json reply, track artist is missing id or name."_s, object_artist);
|
||||
return;
|
||||
}
|
||||
QString artist_id;
|
||||
if (obj_artist["id"_L1].isString()) {
|
||||
artist_id = obj_artist["id"_L1].toString();
|
||||
if (object_artist["id"_L1].isString()) {
|
||||
artist_id = object_artist["id"_L1].toString();
|
||||
}
|
||||
else {
|
||||
artist_id = QString::number(obj_artist["id"_L1].toInt());
|
||||
artist_id = QString::number(object_artist["id"_L1].toInt());
|
||||
}
|
||||
QString artist = obj_artist["name"_L1].toString();
|
||||
QString artist = object_artist["name"_L1].toString();
|
||||
|
||||
if (!value_album.isObject()) {
|
||||
Error(u"Invalid Json reply, track album is not a object."_s, value_album);
|
||||
return;
|
||||
}
|
||||
QJsonObject obj_album = value_album.toObject();
|
||||
if (!obj_album.contains("id"_L1) || !obj_album.contains("title"_L1)) {
|
||||
Error(u"Invalid Json reply, track album is missing ID or title."_s, obj_album);
|
||||
const QJsonObject object_album = value_album.toObject();
|
||||
if (!object_album.contains("id"_L1) || !object_album.contains("title"_L1)) {
|
||||
Error(u"Invalid Json reply, track album is missing ID or title."_s, object_album);
|
||||
return;
|
||||
}
|
||||
QString album_id;
|
||||
if (obj_album["id"_L1].isString()) {
|
||||
album_id = obj_album["id"_L1].toString();
|
||||
if (object_album["id"_L1].isString()) {
|
||||
album_id = object_album["id"_L1].toString();
|
||||
}
|
||||
else {
|
||||
album_id = QString::number(obj_album["id"_L1].toInt());
|
||||
album_id = QString::number(object_album["id"_L1].toInt());
|
||||
}
|
||||
if (!album.album_id.isEmpty() && album.album_id != album_id) {
|
||||
Error(u"Invalid Json reply, track album id is wrong."_s, obj_album);
|
||||
Error(u"Invalid Json reply, track album id is wrong."_s, object_album);
|
||||
return;
|
||||
}
|
||||
QString album_title = obj_album["title"_L1].toString();
|
||||
QString album_title = object_album["title"_L1].toString();
|
||||
if (album.album_explicit) album_title.append(" (Explicit)"_L1);
|
||||
|
||||
if (!allow_streaming) {
|
||||
@@ -1104,8 +1070,8 @@ void TidalRequest::ParseSong(Song &song, const QJsonObject &json_obj, const Arti
|
||||
}
|
||||
|
||||
QUrl cover_url;
|
||||
if (obj_album.contains("cover"_L1)) {
|
||||
const QString cover = obj_album["cover"_L1].toString().replace(u'-', u'/');
|
||||
if (object_album.contains("cover"_L1)) {
|
||||
const QString cover = object_album["cover"_L1].toString().replace(u'-', u'/');
|
||||
if (!cover.isEmpty()) {
|
||||
cover_url.setUrl(QStringLiteral("%1/images/%2/%3.jpg").arg(QLatin1String(kResourcesUrl), cover, coversize_));
|
||||
}
|
||||
@@ -1207,11 +1173,7 @@ void TidalRequest::FlushAlbumCoverRequests() {
|
||||
while (!album_cover_requests_queue_.isEmpty() && album_covers_requests_active_ < kMaxConcurrentAlbumCoverRequests) {
|
||||
|
||||
AlbumCoverRequest request = album_cover_requests_queue_.dequeue();
|
||||
|
||||
QNetworkRequest req(request.url);
|
||||
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||
QNetworkReply *reply = network_->get(req);
|
||||
album_cover_replies_ << reply;
|
||||
QNetworkReply *reply = CreateGetRequest(request.url);
|
||||
QObject::connect(reply, &QNetworkReply::finished, this, [this, reply, request]() { AlbumCoverReceived(reply, request.album_id, request.url, request.filename); });
|
||||
|
||||
++album_covers_requests_active_;
|
||||
@@ -1222,8 +1184,8 @@ void TidalRequest::FlushAlbumCoverRequests() {
|
||||
|
||||
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);
|
||||
if (replies_.contains(reply)) {
|
||||
replies_.removeAll(reply);
|
||||
QObject::disconnect(reply, nullptr, this, nullptr);
|
||||
reply->deleteLater();
|
||||
}
|
||||
@@ -1237,24 +1199,23 @@ void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album
|
||||
|
||||
if (finished_) return;
|
||||
|
||||
const QScopeGuard finish_check = qScopeGuard([this]() { AlbumCoverFinishCheck(); });
|
||||
|
||||
Q_EMIT UpdateProgress(query_id_, GetProgress(album_covers_requests_received_, album_covers_requests_total_));
|
||||
|
||||
if (!album_covers_requests_sent_.contains(album_id)) {
|
||||
AlbumCoverFinishCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
Error(QStringLiteral("%1 (%2)").arg(reply->errorString()).arg(reply->error()));
|
||||
album_covers_requests_sent_.remove(album_id);
|
||||
AlbumCoverFinishCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) {
|
||||
Error(QStringLiteral("Received HTTP code %1 for %2.").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()).arg(url.toString()));
|
||||
if (album_covers_requests_sent_.contains(album_id)) album_covers_requests_sent_.remove(album_id);
|
||||
AlbumCoverFinishCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1265,7 +1226,6 @@ void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album
|
||||
if (!ImageUtils::SupportedImageMimeTypes().contains(mimetype, Qt::CaseInsensitive) && !ImageUtils::SupportedImageFormats().contains(mimetype, Qt::CaseInsensitive)) {
|
||||
Error(QStringLiteral("Unsupported mimetype for image reader %1 for %2").arg(mimetype, url.toString()));
|
||||
if (album_covers_requests_sent_.contains(album_id)) album_covers_requests_sent_.remove(album_id);
|
||||
AlbumCoverFinishCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1273,7 +1233,6 @@ void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album
|
||||
if (data.isEmpty()) {
|
||||
Error(QStringLiteral("Received empty image data for %1").arg(url.toString()));
|
||||
if (album_covers_requests_sent_.contains(album_id)) album_covers_requests_sent_.remove(album_id);
|
||||
AlbumCoverFinishCheck();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1303,8 +1262,6 @@ void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album
|
||||
Error(QStringLiteral("Error decoding image data from %1").arg(url.toString()));
|
||||
}
|
||||
|
||||
AlbumCoverFinishCheck();
|
||||
|
||||
}
|
||||
|
||||
void TidalRequest::AlbumCoverFinishCheck() {
|
||||
@@ -1332,13 +1289,13 @@ void TidalRequest::FinishCheck() {
|
||||
artist_albums_requests_active_ <= 0 &&
|
||||
album_songs_requests_active_ <= 0 &&
|
||||
album_covers_requests_active_ <= 0
|
||||
) {
|
||||
) {
|
||||
if (timer_flush_requests_->isActive()) {
|
||||
timer_flush_requests_->stop();
|
||||
}
|
||||
finished_ = true;
|
||||
if (songs_.isEmpty()) {
|
||||
if (errors_.isEmpty()) {
|
||||
if (error_.isEmpty()) {
|
||||
if (IsSearch()) {
|
||||
Q_EMIT Results(query_id_, SongMap(), tr("No match."));
|
||||
}
|
||||
@@ -1347,7 +1304,7 @@ void TidalRequest::FinishCheck() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
Q_EMIT Results(query_id_, SongMap(), ErrorsToHTML(errors_));
|
||||
Q_EMIT Results(query_id_, SongMap(), error_);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1363,22 +1320,20 @@ int TidalRequest::GetProgress(const int count, const int total) {
|
||||
|
||||
}
|
||||
|
||||
void TidalRequest::Error(const QString &error, const QVariant &debug) {
|
||||
void TidalRequest::Error(const QString &error_message, const QVariant &debug_output) {
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
errors_ << error;
|
||||
qLog(Error) << "Tidal:" << error;
|
||||
qLog(Error) << "Tidal:" << error_message;
|
||||
if (debug_output.isValid()) {
|
||||
qLog(Debug) << debug_output;
|
||||
}
|
||||
|
||||
if (debug.isValid()) qLog(Debug) << debug;
|
||||
|
||||
FinishCheck();
|
||||
error_ = error_message;
|
||||
|
||||
}
|
||||
|
||||
void TidalRequest::Warn(const QString &error, const QVariant &debug) {
|
||||
void TidalRequest::Warn(const QString &error_message, const QVariant &debug) {
|
||||
|
||||
qLog(Error) << "Tidal:" << error;
|
||||
qLog(Warning) << "Tidal:" << error_message;
|
||||
if (debug.isValid()) qLog(Debug) << debug;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user