Refactor subsonic, tidal and qobuz code

This commit is contained in:
Jonas Kvinge
2021-07-01 02:01:38 +02:00
parent b9f3f80d50
commit b5f4df0912
31 changed files with 183 additions and 203 deletions

View File

@@ -97,7 +97,7 @@ bool QobuzCoverProvider::StartSearch(const QString &artist, const QString &album
url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
} }
QUrl url(QobuzBaseRequest::kApiUrl + QString("/") + resource); QUrl url(QobuzService::kApiUrl + QString("/") + resource);
url.setQuery(url_query); url.setQuery(url_query);
QNetworkRequest req(url); QNetworkRequest req(url);

View File

@@ -48,8 +48,6 @@
#include "jsoncoverprovider.h" #include "jsoncoverprovider.h"
#include "tidalcoverprovider.h" #include "tidalcoverprovider.h"
const char *TidalCoverProvider::kApiUrl = "https://api.tidalhifi.com/v1";
const char *TidalCoverProvider::kResourcesUrl = "https://resources.tidal.com";
const int TidalCoverProvider::kLimit = 10; const int TidalCoverProvider::kLimit = 10;
TidalCoverProvider::TidalCoverProvider(Application *app, QObject *parent) : TidalCoverProvider::TidalCoverProvider(Application *app, QObject *parent) :
@@ -103,7 +101,7 @@ bool TidalCoverProvider::StartSearch(const QString &artist, const QString &album
url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
} }
QUrl url(kApiUrl + QString("/") + resource); QUrl url(TidalService::kApiUrl + QString("/") + resource);
url.setQuery(url_query); url.setQuery(url_query);
QNetworkRequest req(url); QNetworkRequest req(url);
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
@@ -271,7 +269,7 @@ void TidalCoverProvider::HandleSearchReply(QNetworkReply *reply, const int id) {
<< qMakePair(QString("750x750"), QSize(750, 750)) << qMakePair(QString("750x750"), QSize(750, 750))
<< qMakePair(QString("640x640"), QSize(640, 640)); << qMakePair(QString("640x640"), QSize(640, 640));
for (const QPair<QString, QSize> &cover_size : cover_sizes) { for (const QPair<QString, QSize> &cover_size : cover_sizes) {
QUrl cover_url(QString("%1/images/%2/%3.jpg").arg(kResourcesUrl, cover, cover_size.first)); QUrl cover_url(QString("%1/images/%2/%3.jpg").arg(TidalService::kResourcesUrl, cover, cover_size.first));
cover_result.image_url = cover_url; cover_result.image_url = cover_url;
cover_result.image_size = cover_size.second; cover_result.image_size = cover_size.second;
results << cover_result; results << cover_result;

View File

@@ -60,8 +60,6 @@ class TidalCoverProvider : public JsonCoverProvider {
void Error(const QString &error, const QVariant &debug = QVariant()) override; void Error(const QString &error, const QVariant &debug = QVariant()) override;
private: private:
static const char *kApiUrl;
static const char *kResourcesUrl;
static const int kLimit; static const int kLimit;
TidalService *service_; TidalService *service_;

View File

@@ -41,8 +41,6 @@
#include "qobuzservice.h" #include "qobuzservice.h"
#include "qobuzbaserequest.h" #include "qobuzbaserequest.h"
const char *QobuzBaseRequest::kApiUrl = "https://www.qobuz.com/api.json/0.2";
QobuzBaseRequest::QobuzBaseRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent) : QobuzBaseRequest::QobuzBaseRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent) :
QObject(parent), QObject(parent),
service_(service), service_(service),
@@ -63,7 +61,7 @@ QNetworkReply *QobuzBaseRequest::CreateRequest(const QString &ressource_name, co
url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
} }
QUrl url(kApiUrl + QString("/") + ressource_name); QUrl url(QobuzService::kApiUrl + QString("/") + ressource_name);
url.setQuery(url_query); url.setQuery(url_query);
QNetworkRequest req(url); QNetworkRequest req(url);
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)

View File

@@ -44,6 +44,8 @@ class QobuzBaseRequest : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit QobuzBaseRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent = nullptr);
~QobuzBaseRequest();
enum QueryType { enum QueryType {
QueryType_None, QueryType_None,
@@ -56,14 +58,10 @@ class QobuzBaseRequest : public QObject {
QueryType_StreamURL, QueryType_StreamURL,
}; };
explicit QobuzBaseRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent); protected:
~QobuzBaseRequest();
typedef QPair<QString, QString> Param; typedef QPair<QString, QString> Param;
typedef QList<Param> ParamList; typedef QList<Param> ParamList;
static const char *kApiUrl;
QNetworkReply *CreateRequest(const QString &ressource_name, const QList<Param> &params_provided); QNetworkReply *CreateRequest(const QString &ressource_name, const QList<Param> &params_provided);
QByteArray GetReplyData(QNetworkReply *reply); QByteArray GetReplyData(QNetworkReply *reply);
QJsonObject ExtractJsonObj(QByteArray &data); QJsonObject ExtractJsonObj(QByteArray &data);
@@ -73,7 +71,6 @@ class QobuzBaseRequest : public QObject {
virtual void Error(const QString &error, const QVariant &debug = QVariant()) = 0; virtual void Error(const QString &error, const QVariant &debug = QVariant()) = 0;
static QString ErrorsToHTML(const QStringList &errors); static QString ErrorsToHTML(const QStringList &errors);
QString api_url() { return QString(kApiUrl); }
QString app_id() { return service_->app_id(); } QString app_id() { return service_->app_id(); }
QString app_secret() { return service_->app_secret(); } QString app_secret() { return service_->app_secret(); }
QString username() { return service_->username(); } QString username() { return service_->username(); }

View File

@@ -38,7 +38,7 @@ class QobuzFavoriteRequest : public QobuzBaseRequest {
Q_OBJECT Q_OBJECT
public: public:
explicit QobuzFavoriteRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent); explicit QobuzFavoriteRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent = nullptr);
~QobuzFavoriteRequest(); ~QobuzFavoriteRequest();
enum FavoriteType { enum FavoriteType {

View File

@@ -904,7 +904,7 @@ void QobuzRequest::SongsReceived(QNetworkReply *reply, const QString &artist_id_
} }
bool compilation = false; bool compilation = false;
//bool multidisc = false; bool multidisc = false;
SongList songs; SongList songs;
int songs_received = 0; int songs_received = 0;
for (const QJsonValueRef value_item : array_items) { for (const QJsonValueRef value_item : array_items) {
@@ -919,18 +919,15 @@ void QobuzRequest::SongsReceived(QNetworkReply *reply, const QString &artist_id_
Song song(Song::Source_Qobuz); Song song(Song::Source_Qobuz);
ParseSong(song, obj_item, artist_id, album_id, album_artist, album, cover_url); ParseSong(song, obj_item, artist_id, album_id, album_artist, album, cover_url);
if (!song.is_valid()) continue; if (!song.is_valid()) continue;
//if (song.disc() >= 2) multidisc = true; if (song.disc() >= 2) multidisc = true;
if (song.is_compilation()) compilation = true; if (song.is_compilation()) compilation = true;
songs << song; songs << song;
} }
for (Song &song : songs) { for (Song song : songs) {
if (compilation) song.set_compilation_detected(true); if (compilation) song.set_compilation_detected(true);
//if (multidisc) { if (!multidisc) song.set_disc(0);
//QString album_full(QString("%1 - (Disc %2)").arg(song.album()).arg(song.disc())); songs_.insert(song.song_id(), song);
//song.set_album(album_full);
//}
songs_ << song;
} }
SongsFinishCheck(artist_id, album_id, limit_requested, offset_requested, songs_total, songs_received, album_artist, album); SongsFinishCheck(artist_id, album_id, limit_requested, offset_requested, songs_total, songs_received, album_artist, album);
@@ -1140,7 +1137,7 @@ QString QobuzRequest::ParseSong(Song &song, const QJsonObject &json_obj, QString
void QobuzRequest::GetAlbumCovers() { void QobuzRequest::GetAlbumCovers() {
for (Song &song : songs_) { for (const Song &song : songs_) {
AddAlbumCoverRequest(song); AddAlbumCoverRequest(song);
} }
FlushAlbumCoverRequests(); FlushAlbumCoverRequests();
@@ -1152,13 +1149,13 @@ void QobuzRequest::GetAlbumCovers() {
} }
void QobuzRequest::AddAlbumCoverRequest(Song &song) { void QobuzRequest::AddAlbumCoverRequest(const Song &song) {
QUrl cover_url = song.art_automatic(); QUrl cover_url = song.art_automatic();
if (!cover_url.isValid()) return; if (!cover_url.isValid()) return;
if (album_covers_requests_sent_.contains(cover_url)) { if (album_covers_requests_sent_.contains(cover_url)) {
album_covers_requests_sent_.insert(cover_url, &song); album_covers_requests_sent_.insert(cover_url, song.song_id());
return; return;
} }
@@ -1167,7 +1164,7 @@ void QobuzRequest::AddAlbumCoverRequest(Song &song) {
request.filename = app_->album_cover_loader()->CoverFilePath(song.source(), song.effective_albumartist(), song.effective_album(), song.album_id(), QString(), cover_url); 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; if (request.filename.isEmpty()) return;
album_covers_requests_sent_.insert(cover_url, &song); album_covers_requests_sent_.insert(cover_url, song.song_id());
++album_covers_requested_; ++album_covers_requested_;
album_cover_requests_queue_.enqueue(request); album_cover_requests_queue_.enqueue(request);
@@ -1261,8 +1258,10 @@ void QobuzRequest::AlbumCoverReceived(QNetworkReply *reply, const QUrl &cover_ur
if (image.loadFromData(data, format)) { if (image.loadFromData(data, format)) {
if (image.save(filename, format)) { if (image.save(filename, format)) {
while (album_covers_requests_sent_.contains(cover_url)) { while (album_covers_requests_sent_.contains(cover_url)) {
Song *song = album_covers_requests_sent_.take(cover_url); const QString song_id = album_covers_requests_sent_.take(cover_url);
song->set_art_automatic(QUrl::fromLocalFile(filename)); if (songs_.contains(song_id)) {
songs_[song_id].set_art_automatic(QUrl::fromLocalFile(filename));
}
} }
} }
else { else {
@@ -1321,9 +1320,9 @@ void QobuzRequest::FinishCheck() {
} }
else { else {
if (songs_.isEmpty() && errors_.isEmpty()) if (songs_.isEmpty() && errors_.isEmpty())
emit Results(query_id_, songs_, tr("Unknown error")); emit Results(query_id_, songs_.values(), tr("Unknown error"));
else else
emit Results(query_id_, songs_, ErrorsToHTML(errors_)); emit Results(query_id_, songs_.values(), ErrorsToHTML(errors_));
} }
} }

View File

@@ -51,7 +51,7 @@ class QobuzRequest : public QobuzBaseRequest {
public: public:
explicit QobuzRequest(QobuzService *service, QobuzUrlHandler *url_handler, Application *app, NetworkAccessManager *network, QueryType type, QObject *parent); explicit QobuzRequest(QobuzService *service, QobuzUrlHandler *url_handler, Application *app, NetworkAccessManager *network, QueryType type, QObject *parent = nullptr);
~QobuzRequest(); ~QobuzRequest();
void ReloadSettings(); void ReloadSettings();
@@ -134,7 +134,7 @@ class QobuzRequest : public QobuzBaseRequest {
QString AlbumCoverFileName(const Song &song); QString AlbumCoverFileName(const Song &song);
void GetAlbumCovers(); void GetAlbumCovers();
void AddAlbumCoverRequest(Song &song); void AddAlbumCoverRequest(const Song &song);
void FlushAlbumCoverRequests(); void FlushAlbumCoverRequests();
void AlbumCoverFinishCheck(); void AlbumCoverFinishCheck();
@@ -170,7 +170,7 @@ class QobuzRequest : public QobuzBaseRequest {
QList<QString> artist_albums_requests_pending_; QList<QString> artist_albums_requests_pending_;
QHash<QString, Request> album_songs_requests_pending_; QHash<QString, Request> album_songs_requests_pending_;
QMultiMap<QUrl, Song*> album_covers_requests_sent_; QMultiMap<QUrl, QString> album_covers_requests_sent_;
int artists_requests_active_; int artists_requests_active_;
int artists_total_; int artists_total_;
@@ -191,7 +191,7 @@ class QobuzRequest : public QobuzBaseRequest {
int album_covers_requested_; int album_covers_requested_;
int album_covers_received_; int album_covers_received_;
SongList songs_; QMap<QString, Song> songs_;
QStringList errors_; QStringList errors_;
bool no_results_; bool no_results_;
QList<QNetworkReply*> replies_; QList<QNetworkReply*> replies_;

View File

@@ -60,6 +60,7 @@
const Song::Source QobuzService::kSource = Song::Source_Qobuz; const Song::Source QobuzService::kSource = Song::Source_Qobuz;
const char *QobuzService::kAuthUrl = "https://www.qobuz.com/api.json/0.2/user/login"; const char *QobuzService::kAuthUrl = "https://www.qobuz.com/api.json/0.2/user/login";
const char *QobuzService::kApiUrl = "https://www.qobuz.com/api.json/0.2";
const int QobuzService::kLoginAttempts = 2; const int QobuzService::kLoginAttempts = 2;
const int QobuzService::kTimeResetLoginAttempts = 60000; const int QobuzService::kTimeResetLoginAttempts = 60000;
@@ -101,8 +102,8 @@ QobuzService::QobuzService(Application *app, QObject *parent)
pending_search_type_(InternetSearchView::SearchType_Artists), pending_search_type_(InternetSearchView::SearchType_Artists),
search_id_(0), search_id_(0),
login_sent_(false), login_sent_(false),
login_attempts_(0) login_attempts_(0),
{ next_stream_url_request_id_(0) {
app->player()->RegisterUrlHandler(url_handler_); app->player()->RegisterUrlHandler(url_handler_);
@@ -176,8 +177,8 @@ QobuzService::QobuzService(Application *app, QObject *parent)
QobuzService::~QobuzService() { QobuzService::~QobuzService() {
while (!stream_url_requests_.isEmpty()) { while (!stream_url_requests_.isEmpty()) {
QobuzStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst(); std::shared_ptr<QobuzStreamURLRequest> stream_url_req = stream_url_requests_.take(stream_url_requests_.firstKey());
QObject::disconnect(stream_url_req, nullptr, this, nullptr); QObject::disconnect(stream_url_req.get(), nullptr, this, nullptr);
stream_url_req->deleteLater(); stream_url_req->deleteLater();
} }
@@ -506,7 +507,7 @@ void QobuzService::GetArtists() {
ResetArtistsRequest(); ResetArtistsRequest();
artists_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Artists, this)); artists_request_ = std::make_shared<QobuzRequest>(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Artists);
QObject::connect(artists_request_.get(), &QobuzRequest::Results, this, &QobuzService::ArtistsResultsReceived); QObject::connect(artists_request_.get(), &QobuzRequest::Results, this, &QobuzService::ArtistsResultsReceived);
QObject::connect(artists_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::ArtistsUpdateStatusReceived); QObject::connect(artists_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::ArtistsUpdateStatusReceived);
@@ -560,7 +561,7 @@ void QobuzService::GetAlbums() {
} }
ResetAlbumsRequest(); ResetAlbumsRequest();
albums_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Albums, this)); albums_request_ = std::make_shared<QobuzRequest>(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Albums);
QObject::connect(albums_request_.get(), &QobuzRequest::Results, this, &QobuzService::AlbumsResultsReceived); QObject::connect(albums_request_.get(), &QobuzRequest::Results, this, &QobuzService::AlbumsResultsReceived);
QObject::connect(albums_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::AlbumsUpdateStatusReceived); QObject::connect(albums_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::AlbumsUpdateStatusReceived);
QObject::connect(albums_request_.get(), &QobuzRequest::ProgressSetMaximum, this, &QobuzService::AlbumsProgressSetMaximumReceived); QObject::connect(albums_request_.get(), &QobuzRequest::ProgressSetMaximum, this, &QobuzService::AlbumsProgressSetMaximumReceived);
@@ -613,7 +614,7 @@ void QobuzService::GetSongs() {
} }
ResetSongsRequest(); ResetSongsRequest();
songs_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Songs, this)); songs_request_ = std::make_shared<QobuzRequest>(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Songs);
QObject::connect(songs_request_.get(), &QobuzRequest::Results, this, &QobuzService::SongsResultsReceived); QObject::connect(songs_request_.get(), &QobuzRequest::Results, this, &QobuzService::SongsResultsReceived);
QObject::connect(songs_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::SongsUpdateStatusReceived); QObject::connect(songs_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::SongsUpdateStatusReceived);
QObject::connect(songs_request_.get(), &QobuzRequest::ProgressSetMaximum, this, &QobuzService::SongsProgressSetMaximumReceived); QObject::connect(songs_request_.get(), &QobuzRequest::ProgressSetMaximum, this, &QobuzService::SongsProgressSetMaximumReceived);
@@ -695,7 +696,7 @@ void QobuzService::SendSearch() {
break; break;
} }
search_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, type, this)); search_request_ = std::make_shared<QobuzRequest>(this, url_handler_, app_, network_, type);
QObject::connect(search_request_.get(), &QobuzRequest::Results, this, &QobuzService::SearchResultsReceived); QObject::connect(search_request_.get(), &QobuzRequest::Results, this, &QobuzService::SearchResultsReceived);
QObject::connect(search_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::SearchUpdateStatus); QObject::connect(search_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::SearchUpdateStatus);
@@ -718,23 +719,22 @@ void QobuzService::GetStreamURL(const QUrl &url) {
return; return;
} }
QobuzStreamURLRequest *stream_url_req = new QobuzStreamURLRequest(this, network_, url, this); const int id = ++next_stream_url_request_id_;
stream_url_requests_ << stream_url_req; std::shared_ptr<QobuzStreamURLRequest> stream_url_req = std::make_shared<QobuzStreamURLRequest>(this, network_, url, id);
stream_url_requests_.insert(id, stream_url_req);
QObject::connect(stream_url_req, &QobuzStreamURLRequest::TryLogin, this, &QobuzService::TryLogin); QObject::connect(stream_url_req.get(), &QobuzStreamURLRequest::TryLogin, this, &QobuzService::TryLogin);
QObject::connect(stream_url_req, &QobuzStreamURLRequest::StreamURLFinished, this, &QobuzService::HandleStreamURLFinished); QObject::connect(stream_url_req.get(), &QobuzStreamURLRequest::StreamURLFinished, this, &QobuzService::HandleStreamURLFinished);
QObject::connect(this, &QobuzService::LoginComplete, stream_url_req, &QobuzStreamURLRequest::LoginComplete); QObject::connect(this, &QobuzService::LoginComplete, stream_url_req.get(), &QobuzStreamURLRequest::LoginComplete);
stream_url_req->Process(); stream_url_req->Process();
} }
void QobuzService::HandleStreamURLFinished(const QUrl &original_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate, const int bit_depth, const qint64 duration, const QString &error) { void QobuzService::HandleStreamURLFinished(const int id, const QUrl &original_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate, const int bit_depth, const qint64 duration, const QString &error) {
QobuzStreamURLRequest *stream_url_req = qobject_cast<QobuzStreamURLRequest*>(sender()); if (!stream_url_requests_.contains(id)) return;
if (!stream_url_req || !stream_url_requests_.contains(stream_url_req)) return; std::shared_ptr<QobuzStreamURLRequest> stream_url_req = stream_url_requests_.take(id);
stream_url_req->deleteLater();
stream_url_requests_.removeAll(stream_url_req);
emit StreamURLFinished(original_url, stream_url, filetype, samplerate, bit_depth, duration, error); emit StreamURLFinished(original_url, stream_url, filetype, samplerate, bit_depth, duration, error);

View File

@@ -29,6 +29,7 @@
#include <QPair> #include <QPair>
#include <QSet> #include <QSet>
#include <QList> #include <QList>
#include <QMap>
#include <QVariant> #include <QVariant>
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
@@ -60,6 +61,7 @@ class QobuzService : public InternetService {
~QobuzService(); ~QobuzService();
static const Song::Source kSource; static const Song::Source kSource;
static const char *kApiUrl;
void Exit() override; void Exit() override;
void ReloadSettings() override; void ReloadSettings() override;
@@ -105,15 +107,6 @@ class QobuzService : public InternetService {
QSortFilterProxyModel *albums_collection_sort_model() override { return albums_collection_sort_model_; } QSortFilterProxyModel *albums_collection_sort_model() override { return albums_collection_sort_model_; }
QSortFilterProxyModel *songs_collection_sort_model() override { return songs_collection_sort_model_; } QSortFilterProxyModel *songs_collection_sort_model() override { return songs_collection_sort_model_; }
enum QueryType {
QueryType_Artists,
QueryType_Albums,
QueryType_Songs,
QueryType_SearchArtists,
QueryType_SearchAlbums,
QueryType_SearchSongs,
};
public slots: public slots:
void ShowConfig() override; void ShowConfig() override;
void TryLogin(); void TryLogin();
@@ -145,7 +138,7 @@ class QobuzService : public InternetService {
void ArtistsUpdateProgressReceived(const int id, const int progress); void ArtistsUpdateProgressReceived(const int id, const int progress);
void AlbumsUpdateProgressReceived(const int id, const int progress); void AlbumsUpdateProgressReceived(const int id, const int progress);
void SongsUpdateProgressReceived(const int id, const int progress); void SongsUpdateProgressReceived(const int id, const int progress);
void HandleStreamURLFinished(const QUrl &original_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate, const int bit_depth, const qint64 duration, const QString &error); void HandleStreamURLFinished(const int id, const QUrl &original_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate, const int bit_depth, const qint64 duration, const QString &error);
private: private:
typedef QPair<QString, QString> Param; typedef QPair<QString, QString> Param;
@@ -155,6 +148,7 @@ class QobuzService : public InternetService {
void LoginError(const QString &error = QString(), const QVariant &debug = QVariant()); void LoginError(const QString &error = QString(), const QVariant &debug = QVariant());
static const char *kAuthUrl; static const char *kAuthUrl;
static const int kLoginAttempts; static const int kLoginAttempts;
static const int kTimeResetLoginAttempts; static const int kTimeResetLoginAttempts;
@@ -217,7 +211,8 @@ class QobuzService : public InternetService {
bool login_sent_; bool login_sent_;
int login_attempts_; int login_attempts_;
QList<QobuzStreamURLRequest*> stream_url_requests_; int next_stream_url_request_id_;
QMap<int, std::shared_ptr<QobuzStreamURLRequest>> stream_url_requests_;
QStringList login_errors_; QStringList login_errors_;

View File

@@ -45,11 +45,12 @@
#include "qobuzbaserequest.h" #include "qobuzbaserequest.h"
#include "qobuzstreamurlrequest.h" #include "qobuzstreamurlrequest.h"
QobuzStreamURLRequest::QobuzStreamURLRequest(QobuzService *service, NetworkAccessManager *network, const QUrl &original_url, QObject *parent) QobuzStreamURLRequest::QobuzStreamURLRequest(QobuzService *service, NetworkAccessManager *network, const QUrl &original_url, const int id, QObject *parent)
: QobuzBaseRequest(service, network, parent), : QobuzBaseRequest(service, network, parent),
service_(service), service_(service),
reply_(nullptr), reply_(nullptr),
original_url_(original_url), original_url_(original_url),
id_(id),
song_id_(original_url.path().toInt()), song_id_(original_url.path().toInt()),
tries_(0), tries_(0),
need_login_(false) {} need_login_(false) {}
@@ -70,7 +71,7 @@ void QobuzStreamURLRequest::LoginComplete(const bool success, const QString &err
need_login_ = false; need_login_ = false;
if (!success) { if (!success) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
return; return;
} }
@@ -81,7 +82,7 @@ void QobuzStreamURLRequest::LoginComplete(const bool success, const QString &err
void QobuzStreamURLRequest::Process() { void QobuzStreamURLRequest::Process() {
if (app_id().isEmpty() || app_secret().isEmpty()) { if (app_id().isEmpty() || app_secret().isEmpty()) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Missing Qobuz app ID or secret.")); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Missing Qobuz app ID or secret."));
return; return;
} }
@@ -100,7 +101,7 @@ void QobuzStreamURLRequest::Cancel() {
reply_->abort(); reply_->abort();
} }
else { else {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Cancelled.")); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Cancelled."));
} }
} }
@@ -171,32 +172,32 @@ void QobuzStreamURLRequest::StreamURLReceived() {
need_login_ = true; need_login_ = true;
return; return;
} }
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
QJsonObject json_obj = ExtractJsonObj(data); QJsonObject json_obj = ExtractJsonObj(data);
if (json_obj.isEmpty()) { if (json_obj.isEmpty()) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
if (!json_obj.contains("track_id")) { if (!json_obj.contains("track_id")) {
Error("Invalid Json reply, stream url is missing track_id.", json_obj); Error("Invalid Json reply, stream url is missing track_id.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
int track_id = json_obj["track_id"].toInt(); int track_id = json_obj["track_id"].toInt();
if (track_id != song_id_) { if (track_id != song_id_) {
Error("Incorrect track ID returned.", json_obj); Error("Incorrect track ID returned.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
if (!json_obj.contains("mime_type") || !json_obj.contains("url")) { if (!json_obj.contains("mime_type") || !json_obj.contains("url")) {
Error("Invalid Json reply, stream url is missing url or mime_type.", json_obj); 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, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
@@ -217,7 +218,7 @@ void QobuzStreamURLRequest::StreamURLReceived() {
if (!url.isValid()) { if (!url.isValid()) {
Error("Returned stream url is invalid.", json_obj); Error("Returned stream url is invalid.", json_obj);
emit StreamURLFinished(original_url_, original_url_, filetype, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, filetype, -1, -1, -1, errors_.first());
return; return;
} }
@@ -234,7 +235,7 @@ void QobuzStreamURLRequest::StreamURLReceived() {
bit_depth = static_cast<int>(json_obj["bit_depth"].toDouble()); bit_depth = static_cast<int>(json_obj["bit_depth"].toDouble());
} }
emit StreamURLFinished(original_url_, url, filetype, samplerate, bit_depth, duration); emit StreamURLFinished(id_, original_url_, url, filetype, samplerate, bit_depth, duration);
} }

View File

@@ -40,7 +40,7 @@ class QobuzStreamURLRequest : public QobuzBaseRequest {
Q_OBJECT Q_OBJECT
public: public:
explicit QobuzStreamURLRequest(QobuzService *service, NetworkAccessManager *network, const QUrl &original_url, QObject *parent); explicit QobuzStreamURLRequest(QobuzService *service, NetworkAccessManager *network, const QUrl &original_url, const int id, QObject *parent = nullptr);
~QobuzStreamURLRequest(); ~QobuzStreamURLRequest();
void GetStreamURL(); void GetStreamURL();
@@ -54,7 +54,7 @@ class QobuzStreamURLRequest : public QobuzBaseRequest {
signals: signals:
void TryLogin(); void TryLogin();
void StreamURLFinished(QUrl original_url, QUrl stream_url, Song::FileType filetype, int samplerate, int bit_depth, qint64 duration, QString error = QString()); void StreamURLFinished(int id, QUrl original_url, QUrl stream_url, Song::FileType filetype, int samplerate, int bit_depth, qint64 duration, QString error = QString());
private slots: private slots:
void StreamURLReceived(); void StreamURLReceived();
@@ -68,6 +68,7 @@ class QobuzStreamURLRequest : public QobuzBaseRequest {
QobuzService *service_; QobuzService *service_;
QNetworkReply *reply_; QNetworkReply *reply_;
QUrl original_url_; QUrl original_url_;
int id_;
int song_id_; int song_id_;
int tries_; int tries_;
bool need_login_; bool need_login_;

View File

@@ -64,8 +64,7 @@ QUrl SubsonicBaseRequest::CreateUrl(const QString &ressource_name, const QList<P
QUrlQuery url_query; QUrlQuery url_query;
for (const Param &param : params) { for (const Param &param : params) {
EncodedParam encoded_param(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
url_query.addQueryItem(encoded_param.first, encoded_param.second);
} }
QUrl url(server_url()); QUrl url(server_url());

View File

@@ -45,14 +45,12 @@ class SubsonicBaseRequest : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit SubsonicBaseRequest(SubsonicService *service, QObject *parent); explicit SubsonicBaseRequest(SubsonicService *service, QObject *parent = nullptr);
protected:
typedef QPair<QString, QString> Param; typedef QPair<QString, QString> Param;
typedef QList<Param> ParamList; typedef QList<Param> ParamList;
typedef QPair<QByteArray, QByteArray> EncodedParam;
typedef QList<EncodedParam> EncodedParamList;
QUrl CreateUrl(const QString &ressource_name, const QList<Param> &params_provided) const; QUrl CreateUrl(const QString &ressource_name, const QList<Param> &params_provided) const;
QNetworkReply *CreateGetRequest(const QString &ressource_name, const QList<Param> &params_provided); QNetworkReply *CreateGetRequest(const QString &ressource_name, const QList<Param> &params_provided);
QByteArray GetReplyData(QNetworkReply *reply); QByteArray GetReplyData(QNetworkReply *reply);

View File

@@ -456,12 +456,12 @@ void SubsonicRequest::AlbumSongsReplyReceived(QNetworkReply *reply, const QStrin
songs << song; songs << song;
} }
for (Song &song : songs) { for (Song song : songs) {
if (compilation) song.set_compilation_detected(true); if (compilation) song.set_compilation_detected(true);
if (!multidisc) { if (!multidisc) {
song.set_disc(0); song.set_disc(0);
} }
songs_ << song; songs_.insert(song.song_id(), song);
} }
SongsFinishCheck(); SongsFinishCheck();
@@ -680,7 +680,7 @@ QString SubsonicRequest::ParseSong(Song &song, const QJsonObject &json_obj, cons
void SubsonicRequest::GetAlbumCovers() { void SubsonicRequest::GetAlbumCovers() {
for (Song &song : songs_) { for (const Song &song : songs_) {
if (!song.art_automatic().isEmpty()) AddAlbumCoverRequest(song); if (!song.art_automatic().isEmpty()) AddAlbumCoverRequest(song);
} }
FlushAlbumCoverRequests(); FlushAlbumCoverRequests();
@@ -692,15 +692,17 @@ void SubsonicRequest::GetAlbumCovers() {
} }
void SubsonicRequest::AddAlbumCoverRequest(Song &song) { void SubsonicRequest::AddAlbumCoverRequest(const Song &song) {
QUrl cover_url(song.art_automatic()); QUrl cover_url(song.art_automatic());
QUrlQuery cover_url_query(cover_url); QUrlQuery cover_url_query(cover_url);
if (!cover_url.isValid()) return; if (!cover_url.isValid()) {
return;
}
if (album_covers_requests_sent_.contains(cover_url)) { if (album_covers_requests_sent_.contains(song.art_automatic())) {
album_covers_requests_sent_.insert(cover_url, &song); album_covers_requests_sent_.insert(song.art_automatic(), song.song_id());
return; return;
} }
@@ -714,7 +716,7 @@ void SubsonicRequest::AddAlbumCoverRequest(Song &song) {
request.filename = cover_path + "/" + cover_url_query.queryItemValue("id") + ".jpg"; request.filename = cover_path + "/" + cover_url_query.queryItemValue("id") + ".jpg";
if (request.filename.isEmpty()) return; if (request.filename.isEmpty()) return;
album_covers_requests_sent_.insert(cover_url, &song); album_covers_requests_sent_.insert(song.art_automatic(), song.song_id());
++album_covers_requested_; ++album_covers_requested_;
album_cover_requests_queue_.enqueue(request); album_cover_requests_queue_.enqueue(request);
@@ -820,8 +822,10 @@ void SubsonicRequest::AlbumCoverReceived(QNetworkReply *reply, const QUrl &url,
if (image.loadFromData(data, format)) { if (image.loadFromData(data, format)) {
if (image.save(filename, format)) { if (image.save(filename, format)) {
while (album_covers_requests_sent_.contains(url)) { while (album_covers_requests_sent_.contains(url)) {
Song *song = album_covers_requests_sent_.take(url); const QString song_id = album_covers_requests_sent_.take(url);
song->set_art_automatic(QUrl::fromLocalFile(filename)); if (songs_.contains(song_id)) {
songs_[song_id].set_art_automatic(QUrl::fromLocalFile(filename));
}
} }
} }
else { else {
@@ -840,8 +844,9 @@ void SubsonicRequest::AlbumCoverReceived(QNetworkReply *reply, const QUrl &url,
void SubsonicRequest::AlbumCoverFinishCheck() { void SubsonicRequest::AlbumCoverFinishCheck() {
if (!album_cover_requests_queue_.isEmpty() && album_covers_requests_active_ < kMaxConcurrentAlbumCoverRequests) if (!album_cover_requests_queue_.isEmpty() && album_covers_requests_active_ < kMaxConcurrentAlbumCoverRequests) {
FlushAlbumCoverRequests(); FlushAlbumCoverRequests();
}
FinishCheck(); FinishCheck();
@@ -868,9 +873,9 @@ void SubsonicRequest::FinishCheck() {
} }
else { else {
if (songs_.isEmpty() && errors_.isEmpty()) if (songs_.isEmpty() && errors_.isEmpty())
emit Results(songs_, tr("Unknown error")); emit Results(songs_.values(), tr("Unknown error"));
else else
emit Results(songs_, ErrorsToHTML(errors_)); emit Results(songs_.values(), ErrorsToHTML(errors_));
} }
} }

View File

@@ -52,7 +52,7 @@ class SubsonicRequest : public SubsonicBaseRequest {
Q_OBJECT Q_OBJECT
public: public:
explicit SubsonicRequest(SubsonicService *service, SubsonicUrlHandler *url_handler, Application *app, QObject *parent); explicit SubsonicRequest(SubsonicService *service, SubsonicUrlHandler *url_handler, Application *app, QObject *parent = nullptr);
~SubsonicRequest() override; ~SubsonicRequest() override;
void ReloadSettings(); void ReloadSettings();
@@ -72,8 +72,6 @@ class SubsonicRequest : public SubsonicBaseRequest {
void AlbumCoverReceived(QNetworkReply *reply, const QUrl &url, const QString &filename); void AlbumCoverReceived(QNetworkReply *reply, const QUrl &url, const QString &filename);
private: private:
typedef QPair<QString, QString> Param;
typedef QList<Param> ParamList;
struct Request { struct Request {
explicit Request() : offset(0), size(0) {} explicit Request() : offset(0), size(0) {}
@@ -103,7 +101,7 @@ class SubsonicRequest : public SubsonicBaseRequest {
QString ParseSong(Song &song, const QJsonObject &json_obj, const QString &artist_id_requested = QString(), const QString &album_id_requested = QString(), const QString &album_artist = QString(), const qint64 album_created = 0); QString ParseSong(Song &song, const QJsonObject &json_obj, const QString &artist_id_requested = QString(), const QString &album_id_requested = QString(), const QString &album_artist = QString(), const qint64 album_created = 0);
void GetAlbumCovers(); void GetAlbumCovers();
void AddAlbumCoverRequest(Song &song); void AddAlbumCoverRequest(const Song &song);
void FlushAlbumCoverRequests(); void FlushAlbumCoverRequests();
void AlbumCoverFinishCheck(); void AlbumCoverFinishCheck();
@@ -128,7 +126,7 @@ class SubsonicRequest : public SubsonicBaseRequest {
QQueue<AlbumCoverRequest> album_cover_requests_queue_; QQueue<AlbumCoverRequest> album_cover_requests_queue_;
QHash<QString, Request> album_songs_requests_pending_; QHash<QString, Request> album_songs_requests_pending_;
QMultiMap<QUrl, Song*> album_covers_requests_sent_; QMultiMap<QUrl, QString> album_covers_requests_sent_;
int albums_requests_active_; int albums_requests_active_;
@@ -140,7 +138,7 @@ class SubsonicRequest : public SubsonicBaseRequest {
int album_covers_requested_; int album_covers_requested_;
int album_covers_received_; int album_covers_received_;
SongList songs_; QMap<QString, Song> songs_;
QStringList errors_; QStringList errors_;
bool no_results_; bool no_results_;
QList<QNetworkReply*> replies_; QList<QNetworkReply*> replies_;

View File

@@ -45,7 +45,7 @@ class SubsonicScrobbleRequest : public SubsonicBaseRequest {
Q_OBJECT Q_OBJECT
public: public:
explicit SubsonicScrobbleRequest(SubsonicService *service, SubsonicUrlHandler *url_handler, Application *app, QObject *parent); explicit SubsonicScrobbleRequest(SubsonicService *service, SubsonicUrlHandler *url_handler, Application *app, QObject *parent = nullptr);
~SubsonicScrobbleRequest() override; ~SubsonicScrobbleRequest() override;
void CreateScrobbleRequest(const QString &song_id, const bool submission, const QDateTime &start_time); void CreateScrobbleRequest(const QString &song_id, const bool submission, const QDateTime &start_time);
@@ -54,8 +54,6 @@ class SubsonicScrobbleRequest : public SubsonicBaseRequest {
void ScrobbleReplyReceived(QNetworkReply *reply); void ScrobbleReplyReceived(QNetworkReply *reply);
private: private:
typedef QPair<QString, QString> Param;
typedef QList<Param> ParamList;
struct Request { struct Request {
explicit Request() : submission(false) {} explicit Request() : submission(false) {}

View File

@@ -68,7 +68,6 @@ const int SubsonicService::kMaxRedirects = 3;
SubsonicService::SubsonicService(Application *app, QObject *parent) SubsonicService::SubsonicService(Application *app, QObject *parent)
: InternetService(Song::Source_Subsonic, "Subsonic", "subsonic", SubsonicSettingsPage::kSettingsGroup, SettingsDialog::Page_Subsonic, app, parent), : InternetService(Song::Source_Subsonic, "Subsonic", "subsonic", SubsonicSettingsPage::kSettingsGroup, SettingsDialog::Page_Subsonic, app, parent),
app_(app), app_(app),
network_(new QNetworkAccessManager),
url_handler_(new SubsonicUrlHandler(app, this)), url_handler_(new SubsonicUrlHandler(app, this)),
collection_backend_(nullptr), collection_backend_(nullptr),
collection_model_(nullptr), collection_model_(nullptr),
@@ -76,12 +75,7 @@ SubsonicService::SubsonicService(Application *app, QObject *parent)
http2_(true), http2_(true),
verify_certificate_(false), verify_certificate_(false),
download_album_covers_(true), download_album_covers_(true),
ping_redirects_(0) ping_redirects_(0) {
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
network_->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
#endif
app->player()->RegisterUrlHandler(url_handler_); app->player()->RegisterUrlHandler(url_handler_);
@@ -153,8 +147,11 @@ void SubsonicService::SendPing() {
void SubsonicService::SendPingWithCredentials(QUrl url, const QString &username, const QString &password, const bool redirect) { void SubsonicService::SendPingWithCredentials(QUrl url, const QString &username, const QString &password, const bool redirect) {
if (!redirect) { if (!network_ || !redirect) {
network_ = std::make_unique<QNetworkAccessManager>(); network_ = std::make_unique<QNetworkAccessManager>();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0))
network_->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
#endif
ping_redirects_ = 0; ping_redirects_ = 0;
} }
@@ -166,10 +163,7 @@ void SubsonicService::SendPingWithCredentials(QUrl url, const QString &username,
QUrlQuery url_query(url.query()); QUrlQuery url_query(url.query());
for (const Param &param : params) { for (const Param &param : params) {
EncodedParam encoded_param(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
if (!url_query.hasQueryItem(encoded_param.first)) {
url_query.addQueryItem(encoded_param.first, encoded_param.second);
}
} }
if (!redirect) { if (!redirect) {
@@ -394,8 +388,8 @@ void SubsonicService::Scrobble(const QString &song_id, const bool submission, co
} }
if (!scrobble_request_) { if (!scrobble_request_) {
// we're doing requests every 30-240s the whole time, so keep reusing this instance // We're doing requests every 30-240s the whole time, so keep reusing this instance
scrobble_request_.reset(new SubsonicScrobbleRequest(this, url_handler_, app_, this)); scrobble_request_ = std::make_shared<SubsonicScrobbleRequest>(this, url_handler_, app_);
} }
scrobble_request_->CreateScrobbleRequest(song_id, submission, time); scrobble_request_->CreateScrobbleRequest(song_id, submission, time);
@@ -425,7 +419,7 @@ void SubsonicService::GetSongs() {
} }
ResetSongsRequest(); ResetSongsRequest();
songs_request_.reset(new SubsonicRequest(this, url_handler_, app_, this)); songs_request_ = std::make_shared<SubsonicRequest>(this, url_handler_, app_);
QObject::connect(songs_request_.get(), &SubsonicRequest::Results, this, &SubsonicService::SongsResultsReceived); QObject::connect(songs_request_.get(), &SubsonicRequest::Results, this, &SubsonicService::SongsResultsReceived);
QObject::connect(songs_request_.get(), &SubsonicRequest::UpdateStatus, this, &SubsonicService::SongsUpdateStatus); QObject::connect(songs_request_.get(), &SubsonicRequest::UpdateStatus, this, &SubsonicService::SongsUpdateStatus);
QObject::connect(songs_request_.get(), &SubsonicRequest::ProgressSetMaximum, this, &SubsonicService::SongsProgressSetMaximum); QObject::connect(songs_request_.get(), &SubsonicRequest::ProgressSetMaximum, this, &SubsonicService::SongsProgressSetMaximum);

View File

@@ -100,9 +100,6 @@ class SubsonicService : public InternetService {
typedef QPair<QString, QString> Param; typedef QPair<QString, QString> Param;
typedef QList<Param> ParamList; typedef QList<Param> ParamList;
typedef QPair<QByteArray, QByteArray> EncodedParam;
typedef QList<EncodedParam> EncodedParamList;
void PingError(const QString &error = QString(), const QVariant &debug = QVariant()); void PingError(const QString &error = QString(), const QVariant &debug = QVariant());
static const char *kClientName; static const char *kClientName;

View File

@@ -54,8 +54,7 @@ UrlHandler::LoadResult SubsonicUrlHandler::StartLoading(const QUrl &url) {
QUrlQuery url_query; QUrlQuery url_query;
for (const Param &param : params) { for (const Param &param : params) {
EncodedParam encoded_param(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
url_query.addQueryItem(encoded_param.first, encoded_param.second);
} }
QUrl stream_url(server_url()); QUrl stream_url(server_url());

View File

@@ -52,9 +52,6 @@ class SubsonicUrlHandler : public UrlHandler {
typedef QPair<QString, QString> Param; typedef QPair<QString, QString> Param;
typedef QList<Param> ParamList; typedef QList<Param> ParamList;
typedef QPair<QByteArray, QByteArray> EncodedParam;
typedef QList<EncodedParam> EncodedParamList;
SubsonicService *service_; SubsonicService *service_;
}; };

View File

@@ -40,8 +40,6 @@
#include "tidalservice.h" #include "tidalservice.h"
#include "tidalbaserequest.h" #include "tidalbaserequest.h"
const char *TidalBaseRequest::kApiUrl = "https://api.tidalhifi.com/v1";
TidalBaseRequest::TidalBaseRequest(TidalService *service, NetworkAccessManager *network, QObject *parent) : TidalBaseRequest::TidalBaseRequest(TidalService *service, NetworkAccessManager *network, QObject *parent) :
QObject(parent), QObject(parent),
service_(service), service_(service),
@@ -58,7 +56,7 @@ QNetworkReply *TidalBaseRequest::CreateRequest(const QString &ressource_name, co
url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
} }
QUrl url(kApiUrl + QString("/") + ressource_name); QUrl url(TidalService::kApiUrl + QString("/") + ressource_name);
url.setQuery(url_query); url.setQuery(url_query);
QNetworkRequest req(url); QNetworkRequest req(url);
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
@@ -128,9 +126,14 @@ QByteArray TidalBaseRequest::GetReplyData(QNetworkReply *reply, const bool send_
service_->Logout(); service_->Logout();
if (!oauth() && send_login && login_attempts() < max_login_attempts() && !api_token().isEmpty() && !username().isEmpty() && !password().isEmpty()) { if (!oauth() && send_login && login_attempts() < max_login_attempts() && !api_token().isEmpty() && !username().isEmpty() && !password().isEmpty()) {
qLog(Error) << "Tidal:" << error; qLog(Error) << "Tidal:" << error;
qLog(Info) << "Tidal:" << "Attempting to login.";
set_need_login(); set_need_login();
emit service_->RequestLogin(); // clazy:exclude=incorrect-emit if (login_sent()) {
qLog(Info) << "Tidal:" << "Waiting for login.";
}
else {
qLog(Info) << "Tidal:" << "Attempting to login.";
emit RequestLogin();
}
} }
else { else {
Error(error); Error(error);

View File

@@ -45,6 +45,7 @@ class TidalBaseRequest : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit TidalBaseRequest(TidalService *service, NetworkAccessManager *network, QObject *parent = nullptr);
enum QueryType { enum QueryType {
QueryType_None, QueryType_None,
@@ -57,8 +58,7 @@ class TidalBaseRequest : public QObject {
QueryType_StreamURL, QueryType_StreamURL,
}; };
explicit TidalBaseRequest(TidalService *service, NetworkAccessManager *network, QObject *parent); protected:
typedef QPair<QString, QString> Param; typedef QPair<QString, QString> Param;
typedef QList<Param> ParamList; typedef QList<Param> ParamList;
@@ -93,12 +93,12 @@ class TidalBaseRequest : public QObject {
virtual void set_need_login() = 0; virtual void set_need_login() = 0;
signals:
void RequestLogin();
private slots: private slots:
void HandleSSLErrors(const QList<QSslError> &ssl_errors); void HandleSSLErrors(const QList<QSslError> &ssl_errors);
protected:
static const char *kApiUrl;
private: private:
TidalService *service_; TidalService *service_;
NetworkAccessManager *network_; NetworkAccessManager *network_;

View File

@@ -133,7 +133,7 @@ void TidalFavoriteRequest::AddFavorites(const FavoriteType type, const SongList
url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
} }
QUrl url(kApiUrl + QString("/") + "users/" + QString::number(service_->user_id()) + "/favorites/" + FavoriteText(type)); QUrl url(TidalService::kApiUrl + QString("/") + "users/" + QString::number(service_->user_id()) + "/favorites/" + FavoriteText(type));
QNetworkRequest req(url); QNetworkRequest req(url);
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
@@ -239,7 +239,7 @@ void TidalFavoriteRequest::RemoveFavorites(const FavoriteType type, const QStrin
url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second));
} }
QUrl url(kApiUrl + QString("/") + "users/" + QString::number(service_->user_id()) + "/favorites/" + FavoriteText(type) + QString("/") + id); QUrl url(TidalService::kApiUrl + QString("/") + "users/" + QString::number(service_->user_id()) + "/favorites/" + FavoriteText(type) + QString("/") + id);
url.setQuery(url_query); url.setQuery(url_query);
QNetworkRequest req(url); QNetworkRequest req(url);
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)

View File

@@ -38,7 +38,7 @@ class TidalFavoriteRequest : public TidalBaseRequest {
Q_OBJECT Q_OBJECT
public: public:
explicit TidalFavoriteRequest(TidalService *service, NetworkAccessManager *network, QObject *parent); explicit TidalFavoriteRequest(TidalService *service, NetworkAccessManager *network, QObject *parent = nullptr);
~TidalFavoriteRequest() override; ~TidalFavoriteRequest() override;
enum FavoriteType { enum FavoriteType {

View File

@@ -887,12 +887,10 @@ void TidalRequest::SongsReceived(QNetworkReply *reply, const QString &artist_id,
songs << song; songs << song;
} }
for (Song &song : songs) { for (Song song : songs) {
if (compilation) song.set_compilation_detected(true); if (compilation) song.set_compilation_detected(true);
if (!multidisc) { if (!multidisc) song.set_disc(0);
song.set_disc(0); songs_.insert(song.song_id(), song);
}
songs_ << song;
} }
SongsFinishCheck(artist_id, album_id, limit_requested, offset_requested, songs_total, songs_received, album_artist, album, album_explicit); SongsFinishCheck(artist_id, album_id, limit_requested, offset_requested, songs_total, songs_received, album_artist, album, album_explicit);
@@ -1110,7 +1108,7 @@ void TidalRequest::GetAlbumCovers() {
void TidalRequest::AddAlbumCoverRequest(Song &song) { void TidalRequest::AddAlbumCoverRequest(Song &song) {
if (album_covers_requests_sent_.contains(song.album_id())) { if (album_covers_requests_sent_.contains(song.album_id())) {
album_covers_requests_sent_.insert(song.album_id(), &song); album_covers_requests_sent_.insert(song.album_id(), song.song_id());
return; return;
} }
@@ -1120,7 +1118,7 @@ void TidalRequest::AddAlbumCoverRequest(Song &song) {
request.filename = app_->album_cover_loader()->CoverFilePath(song.source(), song.effective_albumartist(), song.effective_album(), song.album_id(), QString(), request.url); 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; if (request.filename.isEmpty()) return;
album_covers_requests_sent_.insert(song.album_id(), &song); album_covers_requests_sent_.insert(song.album_id(), song.song_id());
++album_covers_requested_; ++album_covers_requested_;
album_cover_requests_queue_.enqueue(request); album_cover_requests_queue_.enqueue(request);
@@ -1215,8 +1213,10 @@ void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album
if (image.loadFromData(data, format)) { if (image.loadFromData(data, format)) {
if (image.save(filename, format)) { if (image.save(filename, format)) {
while (album_covers_requests_sent_.contains(album_id)) { while (album_covers_requests_sent_.contains(album_id)) {
Song *song = album_covers_requests_sent_.take(album_id); const QString song_id = album_covers_requests_sent_.take(album_id);
song->set_art_automatic(QUrl::fromLocalFile(filename)); if (songs_.contains(song_id)) {
songs_[song_id].set_art_automatic(QUrl::fromLocalFile(filename));
}
} }
} }
else { else {
@@ -1276,9 +1276,9 @@ void TidalRequest::FinishCheck() {
} }
else { else {
if (songs_.isEmpty() && errors_.isEmpty()) if (songs_.isEmpty() && errors_.isEmpty())
emit Results(query_id_, songs_, tr("Unknown error")); emit Results(query_id_, songs_.values(), tr("Unknown error"));
else else
emit Results(query_id_, songs_, ErrorsToHTML(errors_)); emit Results(query_id_, songs_.values(), ErrorsToHTML(errors_));
} }
} }

View File

@@ -178,7 +178,7 @@ class TidalRequest : public TidalBaseRequest {
QList<QString> artist_albums_requests_pending_; QList<QString> artist_albums_requests_pending_;
QHash<QString, Request> album_songs_requests_pending_; QHash<QString, Request> album_songs_requests_pending_;
QMultiMap<QString, Song*> album_covers_requests_sent_; QMultiMap<QString, QString> album_covers_requests_sent_;
int artists_requests_active_; int artists_requests_active_;
int artists_total_; int artists_total_;
@@ -199,7 +199,7 @@ class TidalRequest : public TidalBaseRequest {
int album_covers_requested_; int album_covers_requested_;
int album_covers_received_; int album_covers_received_;
SongList songs_; QMap<QString, Song> songs_;
QStringList errors_; QStringList errors_;
bool need_login_; bool need_login_;
bool no_results_; bool no_results_;

View File

@@ -67,6 +67,8 @@ const char *TidalService::kOAuthUrl = "https://login.tidal.com/authorize";
const char *TidalService::kOAuthAccessTokenUrl = "https://login.tidal.com/oauth2/token"; const char *TidalService::kOAuthAccessTokenUrl = "https://login.tidal.com/oauth2/token";
const char *TidalService::kOAuthRedirectUrl = "tidal://login/auth"; const char *TidalService::kOAuthRedirectUrl = "tidal://login/auth";
const char *TidalService::kAuthUrl = "https://api.tidalhifi.com/v1/login/username"; const char *TidalService::kAuthUrl = "https://api.tidalhifi.com/v1/login/username";
const char *TidalService::kApiUrl = "https://api.tidalhifi.com/v1";
const char *TidalService::kResourcesUrl = "https://resources.tidal.com";
const int TidalService::kLoginAttempts = 2; const int TidalService::kLoginAttempts = 2;
const int TidalService::kTimeResetLoginAttempts = 60000; const int TidalService::kTimeResetLoginAttempts = 60000;
@@ -113,8 +115,8 @@ TidalService::TidalService(Application *app, QObject *parent)
pending_search_type_(InternetSearchView::SearchType_Artists), pending_search_type_(InternetSearchView::SearchType_Artists),
search_id_(0), search_id_(0),
login_sent_(false), login_sent_(false),
login_attempts_(0) login_attempts_(0),
{ next_stream_url_request_id_(0) {
app->player()->RegisterUrlHandler(url_handler_); app->player()->RegisterUrlHandler(url_handler_);
@@ -177,6 +179,8 @@ TidalService::TidalService(Application *app, QObject *parent)
QObject::connect(this, &TidalService::RemoveAlbums, favorite_request_, &TidalFavoriteRequest::RemoveAlbums); QObject::connect(this, &TidalService::RemoveAlbums, favorite_request_, &TidalFavoriteRequest::RemoveAlbums);
QObject::connect(this, &TidalService::RemoveSongs, favorite_request_, &TidalFavoriteRequest::RemoveSongs); QObject::connect(this, &TidalService::RemoveSongs, favorite_request_, &TidalFavoriteRequest::RemoveSongs);
QObject::connect(favorite_request_, &TidalFavoriteRequest::RequestLogin, this, &TidalService::SendLogin);
QObject::connect(favorite_request_, &TidalFavoriteRequest::ArtistsAdded, artists_collection_backend_, &CollectionBackend::AddOrUpdateSongs); QObject::connect(favorite_request_, &TidalFavoriteRequest::ArtistsAdded, artists_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
QObject::connect(favorite_request_, &TidalFavoriteRequest::AlbumsAdded, albums_collection_backend_, &CollectionBackend::AddOrUpdateSongs); QObject::connect(favorite_request_, &TidalFavoriteRequest::AlbumsAdded, albums_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
QObject::connect(favorite_request_, &TidalFavoriteRequest::SongsAdded, songs_collection_backend_, &CollectionBackend::AddOrUpdateSongs); QObject::connect(favorite_request_, &TidalFavoriteRequest::SongsAdded, songs_collection_backend_, &CollectionBackend::AddOrUpdateSongs);
@@ -200,8 +204,8 @@ TidalService::~TidalService() {
} }
while (!stream_url_requests_.isEmpty()) { while (!stream_url_requests_.isEmpty()) {
TidalStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst(); std::shared_ptr<TidalStreamURLRequest> stream_url_req = stream_url_requests_.take(stream_url_requests_.firstKey());
QObject::disconnect(stream_url_req, nullptr, this, nullptr); QObject::disconnect(stream_url_req.get(), nullptr, this, nullptr);
stream_url_req->deleteLater(); stream_url_req->deleteLater();
} }
@@ -745,8 +749,9 @@ void TidalService::GetArtists() {
ResetArtistsRequest(); ResetArtistsRequest();
artists_request_.reset(new TidalRequest(this, url_handler_, app_, network_, TidalBaseRequest::QueryType_Artists, this)); artists_request_ = std::make_shared<TidalRequest>(this, url_handler_, app_, network_, TidalBaseRequest::QueryType_Artists, this);
QObject::connect(artists_request_.get(), &TidalRequest::RequestLogin, this, &TidalService::SendLogin);
QObject::connect(artists_request_.get(), &TidalRequest::Results, this, &TidalService::ArtistsResultsReceived); QObject::connect(artists_request_.get(), &TidalRequest::Results, this, &TidalService::ArtistsResultsReceived);
QObject::connect(artists_request_.get(), &TidalRequest::UpdateStatus, this, &TidalService::ArtistsUpdateStatusReceived); QObject::connect(artists_request_.get(), &TidalRequest::UpdateStatus, this, &TidalService::ArtistsUpdateStatusReceived);
QObject::connect(artists_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::ArtistsProgressSetMaximumReceived); QObject::connect(artists_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::ArtistsProgressSetMaximumReceived);
@@ -803,7 +808,8 @@ void TidalService::GetAlbums() {
} }
ResetAlbumsRequest(); ResetAlbumsRequest();
albums_request_.reset(new TidalRequest(this, url_handler_, app_, network_, TidalBaseRequest::QueryType_Albums, this)); albums_request_ = std::make_shared<TidalRequest>(this, url_handler_, app_, network_, TidalBaseRequest::QueryType_Albums, this);
QObject::connect(albums_request_.get(), &TidalRequest::RequestLogin, this, &TidalService::SendLogin);
QObject::connect(albums_request_.get(), &TidalRequest::Results, this, &TidalService::AlbumsResultsReceived); QObject::connect(albums_request_.get(), &TidalRequest::Results, this, &TidalService::AlbumsResultsReceived);
QObject::connect(albums_request_.get(), &TidalRequest::UpdateStatus, this, &TidalService::AlbumsUpdateStatusReceived); QObject::connect(albums_request_.get(), &TidalRequest::UpdateStatus, this, &TidalService::AlbumsUpdateStatusReceived);
QObject::connect(albums_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::AlbumsProgressSetMaximumReceived); QObject::connect(albums_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::AlbumsProgressSetMaximumReceived);
@@ -860,7 +866,8 @@ void TidalService::GetSongs() {
} }
ResetSongsRequest(); ResetSongsRequest();
songs_request_.reset(new TidalRequest(this, url_handler_, app_, network_, TidalBaseRequest::QueryType_Songs, this)); songs_request_ = std::make_shared<TidalRequest>(this, url_handler_, app_, network_, TidalBaseRequest::QueryType_Songs, this);
QObject::connect(songs_request_.get(), &TidalRequest::RequestLogin, this, &TidalService::SendLogin);
QObject::connect(songs_request_.get(), &TidalRequest::Results, this, &TidalService::SongsResultsReceived); QObject::connect(songs_request_.get(), &TidalRequest::Results, this, &TidalService::SongsResultsReceived);
QObject::connect(songs_request_.get(), &TidalRequest::UpdateStatus, this, &TidalService::SongsUpdateStatusReceived); QObject::connect(songs_request_.get(), &TidalRequest::UpdateStatus, this, &TidalService::SongsUpdateStatusReceived);
QObject::connect(songs_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::SongsProgressSetMaximumReceived); QObject::connect(songs_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::SongsProgressSetMaximumReceived);
@@ -953,8 +960,9 @@ void TidalService::SendSearch() {
return; return;
} }
search_request_.reset(new TidalRequest(this, url_handler_, app_, network_, type, this)); search_request_ = std::make_shared<TidalRequest>(this, url_handler_, app_, network_, type, this);
QObject::connect(search_request_.get(), &TidalRequest::RequestLogin, this, &TidalService::SendLogin);
QObject::connect(search_request_.get(), &TidalRequest::Results, this, &TidalService::SearchResultsReceived); QObject::connect(search_request_.get(), &TidalRequest::Results, this, &TidalService::SearchResultsReceived);
QObject::connect(search_request_.get(), &TidalRequest::UpdateStatus, this, &TidalService::SearchUpdateStatus); QObject::connect(search_request_.get(), &TidalRequest::UpdateStatus, this, &TidalService::SearchUpdateStatus);
QObject::connect(search_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::SearchProgressSetMaximum); QObject::connect(search_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::SearchProgressSetMaximum);
@@ -983,23 +991,22 @@ void TidalService::GetStreamURL(const QUrl &url) {
} }
} }
TidalStreamURLRequest *stream_url_req = new TidalStreamURLRequest(this, network_, url, this); const int id = ++next_stream_url_request_id_;
stream_url_requests_ << stream_url_req; std::shared_ptr<TidalStreamURLRequest> stream_url_req = std::make_shared<TidalStreamURLRequest>(this, network_, url, id);
stream_url_requests_.insert(id, stream_url_req);
QObject::connect(stream_url_req, &TidalStreamURLRequest::TryLogin, this, &TidalService::TryLogin); QObject::connect(stream_url_req.get(), &TidalStreamURLRequest::TryLogin, this, &TidalService::TryLogin);
QObject::connect(stream_url_req, &TidalStreamURLRequest::StreamURLFinished, this, &TidalService::HandleStreamURLFinished); QObject::connect(stream_url_req.get(), &TidalStreamURLRequest::StreamURLFinished, this, &TidalService::HandleStreamURLFinished);
QObject::connect(this, &TidalService::LoginComplete, stream_url_req, &TidalStreamURLRequest::LoginComplete); QObject::connect(this, &TidalService::LoginComplete, stream_url_req.get(), &TidalStreamURLRequest::LoginComplete);
stream_url_req->Process(); stream_url_req->Process();
} }
void TidalService::HandleStreamURLFinished(const QUrl &original_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate, const int bit_depth, const qint64 duration, const QString &error) { void TidalService::HandleStreamURLFinished(const int id, const QUrl &original_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate, const int bit_depth, const qint64 duration, const QString &error) {
TidalStreamURLRequest *stream_url_req = qobject_cast<TidalStreamURLRequest*>(sender()); if (!stream_url_requests_.contains(id)) return;
if (!stream_url_req || !stream_url_requests_.contains(stream_url_req)) return; std::shared_ptr<TidalStreamURLRequest> stream_url_req = stream_url_requests_.take(id);
stream_url_req->deleteLater();
stream_url_requests_.removeAll(stream_url_req);
emit StreamURLFinished(original_url, stream_url, filetype, samplerate, bit_depth, duration, error); emit StreamURLFinished(original_url, stream_url, filetype, samplerate, bit_depth, duration, error);

View File

@@ -29,6 +29,7 @@
#include <QPair> #include <QPair>
#include <QSet> #include <QSet>
#include <QList> #include <QList>
#include <QMap>
#include <QVariant> #include <QVariant>
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
@@ -63,6 +64,8 @@ class TidalService : public InternetService {
~TidalService() override; ~TidalService() override;
static const Song::Source kSource; static const Song::Source kSource;
static const char *kApiUrl;
static const char *kResourcesUrl;
void Exit() override; void Exit() override;
void ReloadSettings() override; void ReloadSettings() override;
@@ -113,15 +116,6 @@ class TidalService : public InternetService {
QSortFilterProxyModel *albums_collection_sort_model() override { return albums_collection_sort_model_; } QSortFilterProxyModel *albums_collection_sort_model() override { return albums_collection_sort_model_; }
QSortFilterProxyModel *songs_collection_sort_model() override { return songs_collection_sort_model_; } QSortFilterProxyModel *songs_collection_sort_model() override { return songs_collection_sort_model_; }
enum QueryType {
QueryType_Artists,
QueryType_Albums,
QueryType_Songs,
QueryType_SearchArtists,
QueryType_SearchAlbums,
QueryType_SearchSongs,
};
public slots: public slots:
void ShowConfig() override; void ShowConfig() override;
void StartAuthorization(const QString &client_id); void StartAuthorization(const QString &client_id);
@@ -157,7 +151,7 @@ class TidalService : public InternetService {
void ArtistsUpdateProgressReceived(const int id, const int progress); void ArtistsUpdateProgressReceived(const int id, const int progress);
void AlbumsUpdateProgressReceived(const int id, const int progress); void AlbumsUpdateProgressReceived(const int id, const int progress);
void SongsUpdateProgressReceived(const int id, const int progress); void SongsUpdateProgressReceived(const int id, const int progress);
void HandleStreamURLFinished(const QUrl &original_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate, const int bit_depth, const qint64 duration, const QString &error = QString()); void HandleStreamURLFinished(const int id, const QUrl &original_url, const QUrl &stream_url, const Song::FileType filetype, const int samplerate, const int bit_depth, const qint64 duration, const QString &error = QString());
private: private:
typedef QPair<QString, QString> Param; typedef QPair<QString, QString> Param;
@@ -172,6 +166,7 @@ class TidalService : public InternetService {
static const char *kOAuthAccessTokenUrl; static const char *kOAuthAccessTokenUrl;
static const char *kOAuthRedirectUrl; static const char *kOAuthRedirectUrl;
static const char *kAuthUrl; static const char *kAuthUrl;
static const int kLoginAttempts; static const int kLoginAttempts;
static const int kTimeResetLoginAttempts; static const int kTimeResetLoginAttempts;
@@ -246,7 +241,8 @@ class TidalService : public InternetService {
QString code_verifier_; QString code_verifier_;
QString code_challenge_; QString code_challenge_;
QList<TidalStreamURLRequest*> stream_url_requests_; int next_stream_url_request_id_;
QMap<int, std::shared_ptr<TidalStreamURLRequest>> stream_url_requests_;
QStringList login_errors_; QStringList login_errors_;

View File

@@ -46,11 +46,12 @@
#include "tidalbaserequest.h" #include "tidalbaserequest.h"
#include "tidalstreamurlrequest.h" #include "tidalstreamurlrequest.h"
TidalStreamURLRequest::TidalStreamURLRequest(TidalService *service, NetworkAccessManager *network, const QUrl &original_url, QObject *parent) TidalStreamURLRequest::TidalStreamURLRequest(TidalService *service, NetworkAccessManager *network, const QUrl &original_url, const int id, QObject *parent)
: TidalBaseRequest(service, network, parent), : TidalBaseRequest(service, network, parent),
service_(service), service_(service),
reply_(nullptr), reply_(nullptr),
original_url_(original_url), original_url_(original_url),
id_(id),
song_id_(original_url.path().toInt()), song_id_(original_url.path().toInt()),
tries_(0), tries_(0),
need_login_(false) {} need_login_(false) {}
@@ -71,7 +72,7 @@ void TidalStreamURLRequest::LoginComplete(const bool success, const QString &err
need_login_ = false; need_login_ = false;
if (!success) { if (!success) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, error);
return; return;
} }
@@ -83,11 +84,11 @@ void TidalStreamURLRequest::Process() {
if (!authenticated()) { if (!authenticated()) {
if (oauth()) { if (oauth()) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Not authenticated with Tidal.")); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Not authenticated with Tidal."));
return; return;
} }
else if (api_token().isEmpty() || username().isEmpty() || password().isEmpty()) { else if (api_token().isEmpty() || username().isEmpty() || password().isEmpty()) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Missing Tidal API token, username or password.")); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Missing Tidal API token, username or password."));
return; return;
} }
need_login_ = true; need_login_ = true;
@@ -105,7 +106,7 @@ void TidalStreamURLRequest::Cancel() {
reply_->abort(); reply_->abort();
} }
else { else {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Cancelled.")); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, tr("Cancelled."));
} }
} }
@@ -160,7 +161,7 @@ void TidalStreamURLRequest::StreamURLReceived() {
need_login_ = true; need_login_ = true;
return; return;
} }
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
reply_ = nullptr; reply_ = nullptr;
@@ -169,19 +170,19 @@ void TidalStreamURLRequest::StreamURLReceived() {
QJsonObject json_obj = ExtractJsonObj(data); QJsonObject json_obj = ExtractJsonObj(data);
if (json_obj.isEmpty()) { if (json_obj.isEmpty()) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
if (!json_obj.contains("trackId")) { if (!json_obj.contains("trackId")) {
Error("Invalid Json reply, stream missing trackId.", json_obj); Error("Invalid Json reply, stream missing trackId.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
int track_id(json_obj["trackId"].toInt()); int track_id(json_obj["trackId"].toInt());
if (track_id != song_id_) { if (track_id != song_id_) {
Error("Incorrect track ID returned.", json_obj); Error("Incorrect track ID returned.", json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
@@ -214,7 +215,7 @@ void TidalStreamURLRequest::StreamURLReceived() {
QString filename = "tidal-" + QString::number(song_id_) + ".xml"; QString filename = "tidal-" + QString::number(song_id_) + ".xml";
if (!QDir().mkpath(filepath)) { if (!QDir().mkpath(filepath)) {
Error(QString("Failed to create directory %1.").arg(filepath), json_obj); Error(QString("Failed to create directory %1.").arg(filepath), json_obj);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
QUrl url("file://" + filepath + "/" + filename); QUrl url("file://" + filepath + "/" + filename);
@@ -223,7 +224,7 @@ void TidalStreamURLRequest::StreamURLReceived() {
file.remove(); file.remove();
if (!file.open(QIODevice::WriteOnly)) { if (!file.open(QIODevice::WriteOnly)) {
Error(QString("Failed to open file %1 for writing.").arg(url.toLocalFile()), json_obj); 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, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
file.write(data_manifest); file.write(data_manifest);
@@ -237,13 +238,13 @@ void TidalStreamURLRequest::StreamURLReceived() {
json_obj = ExtractJsonObj(data_manifest); json_obj = ExtractJsonObj(data_manifest);
if (json_obj.isEmpty()) { if (json_obj.isEmpty()) {
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
if (!json_obj.contains("mimeType")) { if (!json_obj.contains("mimeType")) {
Error("Invalid Json reply, stream url reply manifest is missing mimeType.", json_obj); 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, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
@@ -266,7 +267,7 @@ void TidalStreamURLRequest::StreamURLReceived() {
QJsonValue json_urls = json_obj["urls"]; QJsonValue json_urls = json_obj["urls"];
if (!json_urls.isArray()) { if (!json_urls.isArray()) {
Error("Invalid Json reply, urls is not an array.", json_urls); Error("Invalid Json reply, urls is not an array.", json_urls);
emit StreamURLFinished(original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, Song::FileType_Stream, -1, -1, -1, errors_.first());
return; return;
} }
QJsonArray json_array_urls = json_urls.toArray(); QJsonArray json_array_urls = json_urls.toArray();
@@ -287,11 +288,11 @@ void TidalStreamURLRequest::StreamURLReceived() {
if (urls.isEmpty()) { if (urls.isEmpty()) {
Error("Missing stream urls.", json_obj); Error("Missing stream urls.", json_obj);
emit StreamURLFinished(original_url_, original_url_, filetype, -1, -1, -1, errors_.first()); emit StreamURLFinished(id_, original_url_, original_url_, filetype, -1, -1, -1, errors_.first());
return; return;
} }
emit StreamURLFinished(original_url_, urls.first(), filetype, -1, -1, -1); emit StreamURLFinished(id_, original_url_, urls.first(), filetype, -1, -1, -1);
} }

View File

@@ -41,7 +41,7 @@ class TidalStreamURLRequest : public TidalBaseRequest {
Q_OBJECT Q_OBJECT
public: public:
explicit TidalStreamURLRequest(TidalService *service, NetworkAccessManager *network, const QUrl &original_url, QObject *parent); explicit TidalStreamURLRequest(TidalService *service, NetworkAccessManager *network, const QUrl &original_url, const int id = 0, QObject *parent = nullptr);
~TidalStreamURLRequest() override; ~TidalStreamURLRequest() override;
void GetStreamURL(); void GetStreamURL();
@@ -58,7 +58,7 @@ class TidalStreamURLRequest : public TidalBaseRequest {
signals: signals:
void TryLogin(); void TryLogin();
void StreamURLFinished(QUrl original_url, QUrl stream_url, Song::FileType filetype, int samplerate, int bit_depth, qint64 duration, QString error = QString()); void StreamURLFinished(int id, QUrl original_url, QUrl stream_url, Song::FileType filetype, int samplerate, int bit_depth, qint64 duration, QString error = QString());
private slots: private slots:
void StreamURLReceived(); void StreamURLReceived();
@@ -72,6 +72,7 @@ class TidalStreamURLRequest : public TidalBaseRequest {
TidalService *service_; TidalService *service_;
QNetworkReply *reply_; QNetworkReply *reply_;
QUrl original_url_; QUrl original_url_;
int id_;
int song_id_; int song_id_;
int tries_; int tries_;
bool need_login_; bool need_login_;