From b5f4df09126faba8dbe0d139721d315045c03c25 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Thu, 1 Jul 2021 02:01:38 +0200 Subject: [PATCH] Refactor subsonic, tidal and qobuz code --- src/covermanager/qobuzcoverprovider.cpp | 2 +- src/covermanager/tidalcoverprovider.cpp | 6 ++-- src/covermanager/tidalcoverprovider.h | 2 -- src/qobuz/qobuzbaserequest.cpp | 4 +-- src/qobuz/qobuzbaserequest.h | 9 ++---- src/qobuz/qobuzfavoriterequest.h | 2 +- src/qobuz/qobuzrequest.cpp | 31 +++++++++--------- src/qobuz/qobuzrequest.h | 8 ++--- src/qobuz/qobuzservice.cpp | 36 ++++++++++----------- src/qobuz/qobuzservice.h | 17 ++++------ src/qobuz/qobuzstreamurlrequest.cpp | 23 ++++++------- src/qobuz/qobuzstreamurlrequest.h | 5 +-- src/subsonic/subsonicbaserequest.cpp | 3 +- src/subsonic/subsonicbaserequest.h | 6 ++-- src/subsonic/subsonicrequest.cpp | 31 ++++++++++-------- src/subsonic/subsonicrequest.h | 10 +++--- src/subsonic/subsonicscrobblerequest.h | 4 +-- src/subsonic/subsonicservice.cpp | 24 ++++++-------- src/subsonic/subsonicservice.h | 3 -- src/subsonic/subsonicurlhandler.cpp | 3 +- src/subsonic/subsonicurlhandler.h | 3 -- src/tidal/tidalbaserequest.cpp | 13 +++++--- src/tidal/tidalbaserequest.h | 10 +++--- src/tidal/tidalfavoriterequest.cpp | 4 +-- src/tidal/tidalfavoriterequest.h | 2 +- src/tidal/tidalrequest.cpp | 22 ++++++------- src/tidal/tidalrequest.h | 4 +-- src/tidal/tidalservice.cpp | 43 ++++++++++++++----------- src/tidal/tidalservice.h | 18 ++++------- src/tidal/tidalstreamurlrequest.cpp | 33 ++++++++++--------- src/tidal/tidalstreamurlrequest.h | 5 +-- 31 files changed, 183 insertions(+), 203 deletions(-) diff --git a/src/covermanager/qobuzcoverprovider.cpp b/src/covermanager/qobuzcoverprovider.cpp index 0ee33fd50..758c45ed6 100644 --- a/src/covermanager/qobuzcoverprovider.cpp +++ b/src/covermanager/qobuzcoverprovider.cpp @@ -97,7 +97,7 @@ bool QobuzCoverProvider::StartSearch(const QString &artist, const QString &album 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); QNetworkRequest req(url); diff --git a/src/covermanager/tidalcoverprovider.cpp b/src/covermanager/tidalcoverprovider.cpp index 7288342b3..33a649e55 100644 --- a/src/covermanager/tidalcoverprovider.cpp +++ b/src/covermanager/tidalcoverprovider.cpp @@ -48,8 +48,6 @@ #include "jsoncoverprovider.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; 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)); } - QUrl url(kApiUrl + QString("/") + resource); + QUrl url(TidalService::kApiUrl + QString("/") + resource); url.setQuery(url_query); QNetworkRequest req(url); #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("640x640"), QSize(640, 640)); for (const QPair &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_size = cover_size.second; results << cover_result; diff --git a/src/covermanager/tidalcoverprovider.h b/src/covermanager/tidalcoverprovider.h index 2856e783c..3bd12e7a5 100644 --- a/src/covermanager/tidalcoverprovider.h +++ b/src/covermanager/tidalcoverprovider.h @@ -60,8 +60,6 @@ class TidalCoverProvider : public JsonCoverProvider { void Error(const QString &error, const QVariant &debug = QVariant()) override; private: - static const char *kApiUrl; - static const char *kResourcesUrl; static const int kLimit; TidalService *service_; diff --git a/src/qobuz/qobuzbaserequest.cpp b/src/qobuz/qobuzbaserequest.cpp index 57864c8bf..31b5f414a 100644 --- a/src/qobuz/qobuzbaserequest.cpp +++ b/src/qobuz/qobuzbaserequest.cpp @@ -41,8 +41,6 @@ #include "qobuzservice.h" #include "qobuzbaserequest.h" -const char *QobuzBaseRequest::kApiUrl = "https://www.qobuz.com/api.json/0.2"; - QobuzBaseRequest::QobuzBaseRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent) : QObject(parent), 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)); } - QUrl url(kApiUrl + QString("/") + ressource_name); + QUrl url(QobuzService::kApiUrl + QString("/") + ressource_name); url.setQuery(url_query); QNetworkRequest req(url); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) diff --git a/src/qobuz/qobuzbaserequest.h b/src/qobuz/qobuzbaserequest.h index 8d8ae3dde..4febb6301 100644 --- a/src/qobuz/qobuzbaserequest.h +++ b/src/qobuz/qobuzbaserequest.h @@ -44,6 +44,8 @@ class QobuzBaseRequest : public QObject { Q_OBJECT public: + explicit QobuzBaseRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent = nullptr); + ~QobuzBaseRequest(); enum QueryType { QueryType_None, @@ -56,14 +58,10 @@ class QobuzBaseRequest : public QObject { QueryType_StreamURL, }; - explicit QobuzBaseRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent); - ~QobuzBaseRequest(); - + protected: typedef QPair Param; typedef QList ParamList; - static const char *kApiUrl; - QNetworkReply *CreateRequest(const QString &ressource_name, const QList ¶ms_provided); QByteArray GetReplyData(QNetworkReply *reply); QJsonObject ExtractJsonObj(QByteArray &data); @@ -73,7 +71,6 @@ class QobuzBaseRequest : public QObject { virtual void Error(const QString &error, const QVariant &debug = QVariant()) = 0; static QString ErrorsToHTML(const QStringList &errors); - QString api_url() { return QString(kApiUrl); } QString app_id() { return service_->app_id(); } QString app_secret() { return service_->app_secret(); } QString username() { return service_->username(); } diff --git a/src/qobuz/qobuzfavoriterequest.h b/src/qobuz/qobuzfavoriterequest.h index d97ebc970..8b1947393 100644 --- a/src/qobuz/qobuzfavoriterequest.h +++ b/src/qobuz/qobuzfavoriterequest.h @@ -38,7 +38,7 @@ class QobuzFavoriteRequest : public QobuzBaseRequest { Q_OBJECT public: - explicit QobuzFavoriteRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent); + explicit QobuzFavoriteRequest(QobuzService *service, NetworkAccessManager *network, QObject *parent = nullptr); ~QobuzFavoriteRequest(); enum FavoriteType { diff --git a/src/qobuz/qobuzrequest.cpp b/src/qobuz/qobuzrequest.cpp index 95e14ea81..5902d3563 100644 --- a/src/qobuz/qobuzrequest.cpp +++ b/src/qobuz/qobuzrequest.cpp @@ -904,7 +904,7 @@ void QobuzRequest::SongsReceived(QNetworkReply *reply, const QString &artist_id_ } bool compilation = false; - //bool multidisc = false; + bool multidisc = false; SongList songs; int songs_received = 0; 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); ParseSong(song, obj_item, artist_id, album_id, album_artist, album, cover_url); if (!song.is_valid()) continue; - //if (song.disc() >= 2) multidisc = true; + if (song.disc() >= 2) multidisc = true; if (song.is_compilation()) compilation = true; songs << song; } - for (Song &song : songs) { + for (Song song : songs) { if (compilation) song.set_compilation_detected(true); - //if (multidisc) { - //QString album_full(QString("%1 - (Disc %2)").arg(song.album()).arg(song.disc())); - //song.set_album(album_full); - //} - songs_ << song; + if (!multidisc) song.set_disc(0); + songs_.insert(song.song_id(), song); } 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() { - for (Song &song : songs_) { + for (const Song &song : songs_) { AddAlbumCoverRequest(song); } 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(); if (!cover_url.isValid()) return; 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; } @@ -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); 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_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.save(filename, format)) { while (album_covers_requests_sent_.contains(cover_url)) { - Song *song = album_covers_requests_sent_.take(cover_url); - song->set_art_automatic(QUrl::fromLocalFile(filename)); + const QString song_id = album_covers_requests_sent_.take(cover_url); + if (songs_.contains(song_id)) { + songs_[song_id].set_art_automatic(QUrl::fromLocalFile(filename)); + } } } else { @@ -1321,9 +1320,9 @@ void QobuzRequest::FinishCheck() { } else { if (songs_.isEmpty() && errors_.isEmpty()) - emit Results(query_id_, songs_, tr("Unknown error")); + emit Results(query_id_, songs_.values(), tr("Unknown error")); else - emit Results(query_id_, songs_, ErrorsToHTML(errors_)); + emit Results(query_id_, songs_.values(), ErrorsToHTML(errors_)); } } diff --git a/src/qobuz/qobuzrequest.h b/src/qobuz/qobuzrequest.h index ac6ada906..a3062a33b 100644 --- a/src/qobuz/qobuzrequest.h +++ b/src/qobuz/qobuzrequest.h @@ -51,7 +51,7 @@ class QobuzRequest : public QobuzBaseRequest { 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(); void ReloadSettings(); @@ -134,7 +134,7 @@ class QobuzRequest : public QobuzBaseRequest { QString AlbumCoverFileName(const Song &song); void GetAlbumCovers(); - void AddAlbumCoverRequest(Song &song); + void AddAlbumCoverRequest(const Song &song); void FlushAlbumCoverRequests(); void AlbumCoverFinishCheck(); @@ -170,7 +170,7 @@ class QobuzRequest : public QobuzBaseRequest { QList artist_albums_requests_pending_; QHash album_songs_requests_pending_; - QMultiMap album_covers_requests_sent_; + QMultiMap album_covers_requests_sent_; int artists_requests_active_; int artists_total_; @@ -191,7 +191,7 @@ class QobuzRequest : public QobuzBaseRequest { int album_covers_requested_; int album_covers_received_; - SongList songs_; + QMap songs_; QStringList errors_; bool no_results_; QList replies_; diff --git a/src/qobuz/qobuzservice.cpp b/src/qobuz/qobuzservice.cpp index 146f6dcbe..45c684897 100644 --- a/src/qobuz/qobuzservice.cpp +++ b/src/qobuz/qobuzservice.cpp @@ -60,6 +60,7 @@ 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::kApiUrl = "https://www.qobuz.com/api.json/0.2"; const int QobuzService::kLoginAttempts = 2; const int QobuzService::kTimeResetLoginAttempts = 60000; @@ -101,8 +102,8 @@ QobuzService::QobuzService(Application *app, QObject *parent) pending_search_type_(InternetSearchView::SearchType_Artists), search_id_(0), login_sent_(false), - login_attempts_(0) - { + login_attempts_(0), + next_stream_url_request_id_(0) { app->player()->RegisterUrlHandler(url_handler_); @@ -176,8 +177,8 @@ QobuzService::QobuzService(Application *app, QObject *parent) QobuzService::~QobuzService() { while (!stream_url_requests_.isEmpty()) { - QobuzStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst(); - QObject::disconnect(stream_url_req, nullptr, this, nullptr); + std::shared_ptr stream_url_req = stream_url_requests_.take(stream_url_requests_.firstKey()); + QObject::disconnect(stream_url_req.get(), nullptr, this, nullptr); stream_url_req->deleteLater(); } @@ -506,7 +507,7 @@ void QobuzService::GetArtists() { ResetArtistsRequest(); - artists_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Artists, this)); + artists_request_ = std::make_shared(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Artists); QObject::connect(artists_request_.get(), &QobuzRequest::Results, this, &QobuzService::ArtistsResultsReceived); QObject::connect(artists_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::ArtistsUpdateStatusReceived); @@ -560,7 +561,7 @@ void QobuzService::GetAlbums() { } ResetAlbumsRequest(); - albums_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Albums, this)); + albums_request_ = std::make_shared(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Albums); 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::ProgressSetMaximum, this, &QobuzService::AlbumsProgressSetMaximumReceived); @@ -613,7 +614,7 @@ void QobuzService::GetSongs() { } ResetSongsRequest(); - songs_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Songs, this)); + songs_request_ = std::make_shared(this, url_handler_, app_, network_, QobuzBaseRequest::QueryType_Songs); 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::ProgressSetMaximum, this, &QobuzService::SongsProgressSetMaximumReceived); @@ -695,7 +696,7 @@ void QobuzService::SendSearch() { break; } - search_request_.reset(new QobuzRequest(this, url_handler_, app_, network_, type, this)); + search_request_ = std::make_shared(this, url_handler_, app_, network_, type); QObject::connect(search_request_.get(), &QobuzRequest::Results, this, &QobuzService::SearchResultsReceived); QObject::connect(search_request_.get(), &QobuzRequest::UpdateStatus, this, &QobuzService::SearchUpdateStatus); @@ -718,23 +719,22 @@ void QobuzService::GetStreamURL(const QUrl &url) { return; } - QobuzStreamURLRequest *stream_url_req = new QobuzStreamURLRequest(this, network_, url, this); - stream_url_requests_ << stream_url_req; + const int id = ++next_stream_url_request_id_; + std::shared_ptr stream_url_req = std::make_shared(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, &QobuzStreamURLRequest::StreamURLFinished, this, &QobuzService::HandleStreamURLFinished); - QObject::connect(this, &QobuzService::LoginComplete, stream_url_req, &QobuzStreamURLRequest::LoginComplete); + QObject::connect(stream_url_req.get(), &QobuzStreamURLRequest::TryLogin, this, &QobuzService::TryLogin); + QObject::connect(stream_url_req.get(), &QobuzStreamURLRequest::StreamURLFinished, this, &QobuzService::HandleStreamURLFinished); + QObject::connect(this, &QobuzService::LoginComplete, stream_url_req.get(), &QobuzStreamURLRequest::LoginComplete); 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(sender()); - if (!stream_url_req || !stream_url_requests_.contains(stream_url_req)) return; - stream_url_req->deleteLater(); - stream_url_requests_.removeAll(stream_url_req); + if (!stream_url_requests_.contains(id)) return; + std::shared_ptr stream_url_req = stream_url_requests_.take(id); emit StreamURLFinished(original_url, stream_url, filetype, samplerate, bit_depth, duration, error); diff --git a/src/qobuz/qobuzservice.h b/src/qobuz/qobuzservice.h index 0a4734f6e..7c25f0281 100644 --- a/src/qobuz/qobuzservice.h +++ b/src/qobuz/qobuzservice.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,7 @@ class QobuzService : public InternetService { ~QobuzService(); static const Song::Source kSource; + static const char *kApiUrl; void Exit() override; void ReloadSettings() override; @@ -105,15 +107,6 @@ class QobuzService : public InternetService { QSortFilterProxyModel *albums_collection_sort_model() override { return albums_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: void ShowConfig() override; void TryLogin(); @@ -145,7 +138,7 @@ class QobuzService : public InternetService { void ArtistsUpdateProgressReceived(const int id, const int progress); void AlbumsUpdateProgressReceived(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: typedef QPair Param; @@ -155,6 +148,7 @@ class QobuzService : public InternetService { void LoginError(const QString &error = QString(), const QVariant &debug = QVariant()); static const char *kAuthUrl; + static const int kLoginAttempts; static const int kTimeResetLoginAttempts; @@ -217,7 +211,8 @@ class QobuzService : public InternetService { bool login_sent_; int login_attempts_; - QList stream_url_requests_; + int next_stream_url_request_id_; + QMap> stream_url_requests_; QStringList login_errors_; diff --git a/src/qobuz/qobuzstreamurlrequest.cpp b/src/qobuz/qobuzstreamurlrequest.cpp index 8572259ac..ffc99911c 100644 --- a/src/qobuz/qobuzstreamurlrequest.cpp +++ b/src/qobuz/qobuzstreamurlrequest.cpp @@ -45,11 +45,12 @@ #include "qobuzbaserequest.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), service_(service), reply_(nullptr), original_url_(original_url), + id_(id), song_id_(original_url.path().toInt()), tries_(0), need_login_(false) {} @@ -70,7 +71,7 @@ void QobuzStreamURLRequest::LoginComplete(const bool success, const QString &err need_login_ = false; 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; } @@ -81,7 +82,7 @@ void QobuzStreamURLRequest::LoginComplete(const bool success, const QString &err void QobuzStreamURLRequest::Process() { 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; } @@ -100,7 +101,7 @@ void QobuzStreamURLRequest::Cancel() { reply_->abort(); } 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; 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; } QJsonObject json_obj = ExtractJsonObj(data); 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; } if (!json_obj.contains("track_id")) { 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; } int track_id = json_obj["track_id"].toInt(); if (track_id != song_id_) { 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; } if (!json_obj.contains("mime_type") || !json_obj.contains("url")) { 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; } @@ -217,7 +218,7 @@ void QobuzStreamURLRequest::StreamURLReceived() { if (!url.isValid()) { 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; } @@ -234,7 +235,7 @@ void QobuzStreamURLRequest::StreamURLReceived() { bit_depth = static_cast(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); } diff --git a/src/qobuz/qobuzstreamurlrequest.h b/src/qobuz/qobuzstreamurlrequest.h index b85128478..c56f74082 100644 --- a/src/qobuz/qobuzstreamurlrequest.h +++ b/src/qobuz/qobuzstreamurlrequest.h @@ -40,7 +40,7 @@ class QobuzStreamURLRequest : public QobuzBaseRequest { Q_OBJECT 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(); void GetStreamURL(); @@ -54,7 +54,7 @@ class QobuzStreamURLRequest : public QobuzBaseRequest { signals: 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: void StreamURLReceived(); @@ -68,6 +68,7 @@ class QobuzStreamURLRequest : public QobuzBaseRequest { QobuzService *service_; QNetworkReply *reply_; QUrl original_url_; + int id_; int song_id_; int tries_; bool need_login_; diff --git a/src/subsonic/subsonicbaserequest.cpp b/src/subsonic/subsonicbaserequest.cpp index ff2247a3a..36731f619 100644 --- a/src/subsonic/subsonicbaserequest.cpp +++ b/src/subsonic/subsonicbaserequest.cpp @@ -64,8 +64,7 @@ QUrl SubsonicBaseRequest::CreateUrl(const QString &ressource_name, const QList

Param; typedef QList ParamList; - typedef QPair EncodedParam; - typedef QList EncodedParamList; - QUrl CreateUrl(const QString &ressource_name, const QList ¶ms_provided) const; QNetworkReply *CreateGetRequest(const QString &ressource_name, const QList ¶ms_provided); QByteArray GetReplyData(QNetworkReply *reply); diff --git a/src/subsonic/subsonicrequest.cpp b/src/subsonic/subsonicrequest.cpp index 57a5be5a3..ca8504d3c 100644 --- a/src/subsonic/subsonicrequest.cpp +++ b/src/subsonic/subsonicrequest.cpp @@ -456,12 +456,12 @@ void SubsonicRequest::AlbumSongsReplyReceived(QNetworkReply *reply, const QStrin songs << song; } - for (Song &song : songs) { + for (Song song : songs) { if (compilation) song.set_compilation_detected(true); if (!multidisc) { song.set_disc(0); } - songs_ << song; + songs_.insert(song.song_id(), song); } SongsFinishCheck(); @@ -680,7 +680,7 @@ QString SubsonicRequest::ParseSong(Song &song, const QJsonObject &json_obj, cons void SubsonicRequest::GetAlbumCovers() { - for (Song &song : songs_) { + for (const Song &song : songs_) { if (!song.art_automatic().isEmpty()) AddAlbumCoverRequest(song); } 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()); QUrlQuery cover_url_query(cover_url); - if (!cover_url.isValid()) return; + if (!cover_url.isValid()) { + return; + } - if (album_covers_requests_sent_.contains(cover_url)) { - album_covers_requests_sent_.insert(cover_url, &song); + if (album_covers_requests_sent_.contains(song.art_automatic())) { + album_covers_requests_sent_.insert(song.art_automatic(), song.song_id()); return; } @@ -714,7 +716,7 @@ void SubsonicRequest::AddAlbumCoverRequest(Song &song) { request.filename = cover_path + "/" + cover_url_query.queryItemValue("id") + ".jpg"; 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_cover_requests_queue_.enqueue(request); @@ -820,8 +822,10 @@ void SubsonicRequest::AlbumCoverReceived(QNetworkReply *reply, const QUrl &url, if (image.loadFromData(data, format)) { if (image.save(filename, format)) { while (album_covers_requests_sent_.contains(url)) { - Song *song = album_covers_requests_sent_.take(url); - song->set_art_automatic(QUrl::fromLocalFile(filename)); + const QString song_id = album_covers_requests_sent_.take(url); + if (songs_.contains(song_id)) { + songs_[song_id].set_art_automatic(QUrl::fromLocalFile(filename)); + } } } else { @@ -840,8 +844,9 @@ void SubsonicRequest::AlbumCoverReceived(QNetworkReply *reply, const QUrl &url, 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(); + } FinishCheck(); @@ -868,9 +873,9 @@ void SubsonicRequest::FinishCheck() { } else { if (songs_.isEmpty() && errors_.isEmpty()) - emit Results(songs_, tr("Unknown error")); + emit Results(songs_.values(), tr("Unknown error")); else - emit Results(songs_, ErrorsToHTML(errors_)); + emit Results(songs_.values(), ErrorsToHTML(errors_)); } } diff --git a/src/subsonic/subsonicrequest.h b/src/subsonic/subsonicrequest.h index 5485eda40..8eb0ddbc3 100644 --- a/src/subsonic/subsonicrequest.h +++ b/src/subsonic/subsonicrequest.h @@ -52,7 +52,7 @@ class SubsonicRequest : public SubsonicBaseRequest { Q_OBJECT 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; void ReloadSettings(); @@ -72,8 +72,6 @@ class SubsonicRequest : public SubsonicBaseRequest { void AlbumCoverReceived(QNetworkReply *reply, const QUrl &url, const QString &filename); private: - typedef QPair Param; - typedef QList ParamList; struct Request { 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); void GetAlbumCovers(); - void AddAlbumCoverRequest(Song &song); + void AddAlbumCoverRequest(const Song &song); void FlushAlbumCoverRequests(); void AlbumCoverFinishCheck(); @@ -128,7 +126,7 @@ class SubsonicRequest : public SubsonicBaseRequest { QQueue album_cover_requests_queue_; QHash album_songs_requests_pending_; - QMultiMap album_covers_requests_sent_; + QMultiMap album_covers_requests_sent_; int albums_requests_active_; @@ -140,7 +138,7 @@ class SubsonicRequest : public SubsonicBaseRequest { int album_covers_requested_; int album_covers_received_; - SongList songs_; + QMap songs_; QStringList errors_; bool no_results_; QList replies_; diff --git a/src/subsonic/subsonicscrobblerequest.h b/src/subsonic/subsonicscrobblerequest.h index 4972fa24a..5ca068846 100644 --- a/src/subsonic/subsonicscrobblerequest.h +++ b/src/subsonic/subsonicscrobblerequest.h @@ -45,7 +45,7 @@ class SubsonicScrobbleRequest : public SubsonicBaseRequest { Q_OBJECT 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; 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); private: - typedef QPair Param; - typedef QList ParamList; struct Request { explicit Request() : submission(false) {} diff --git a/src/subsonic/subsonicservice.cpp b/src/subsonic/subsonicservice.cpp index a4ac5b1c0..e6859ade1 100644 --- a/src/subsonic/subsonicservice.cpp +++ b/src/subsonic/subsonicservice.cpp @@ -68,7 +68,6 @@ const int SubsonicService::kMaxRedirects = 3; SubsonicService::SubsonicService(Application *app, QObject *parent) : InternetService(Song::Source_Subsonic, "Subsonic", "subsonic", SubsonicSettingsPage::kSettingsGroup, SettingsDialog::Page_Subsonic, app, parent), app_(app), - network_(new QNetworkAccessManager), url_handler_(new SubsonicUrlHandler(app, this)), collection_backend_(nullptr), collection_model_(nullptr), @@ -76,12 +75,7 @@ SubsonicService::SubsonicService(Application *app, QObject *parent) http2_(true), verify_certificate_(false), download_album_covers_(true), - ping_redirects_(0) - { - -#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) - network_->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); -#endif + ping_redirects_(0) { 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) { - if (!redirect) { + if (!network_ || !redirect) { network_ = std::make_unique(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) + network_->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); +#endif ping_redirects_ = 0; } @@ -166,10 +163,7 @@ void SubsonicService::SendPingWithCredentials(QUrl url, const QString &username, QUrlQuery url_query(url.query()); for (const Param ¶m : params) { - EncodedParam encoded_param(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); - if (!url_query.hasQueryItem(encoded_param.first)) { - url_query.addQueryItem(encoded_param.first, encoded_param.second); - } + url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); } if (!redirect) { @@ -394,8 +388,8 @@ void SubsonicService::Scrobble(const QString &song_id, const bool submission, co } if (!scrobble_request_) { - // 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)); + // We're doing requests every 30-240s the whole time, so keep reusing this instance + scrobble_request_ = std::make_shared(this, url_handler_, app_); } scrobble_request_->CreateScrobbleRequest(song_id, submission, time); @@ -425,7 +419,7 @@ void SubsonicService::GetSongs() { } ResetSongsRequest(); - songs_request_.reset(new SubsonicRequest(this, url_handler_, app_, this)); + songs_request_ = std::make_shared(this, url_handler_, app_); 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::ProgressSetMaximum, this, &SubsonicService::SongsProgressSetMaximum); diff --git a/src/subsonic/subsonicservice.h b/src/subsonic/subsonicservice.h index 47d5a8fe3..80085d84d 100644 --- a/src/subsonic/subsonicservice.h +++ b/src/subsonic/subsonicservice.h @@ -100,9 +100,6 @@ class SubsonicService : public InternetService { typedef QPair Param; typedef QList ParamList; - typedef QPair EncodedParam; - typedef QList EncodedParamList; - void PingError(const QString &error = QString(), const QVariant &debug = QVariant()); static const char *kClientName; diff --git a/src/subsonic/subsonicurlhandler.cpp b/src/subsonic/subsonicurlhandler.cpp index af0041421..d229d43fb 100644 --- a/src/subsonic/subsonicurlhandler.cpp +++ b/src/subsonic/subsonicurlhandler.cpp @@ -54,8 +54,7 @@ UrlHandler::LoadResult SubsonicUrlHandler::StartLoading(const QUrl &url) { QUrlQuery url_query; for (const Param ¶m : params) { - EncodedParam encoded_param(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); - url_query.addQueryItem(encoded_param.first, encoded_param.second); + url_query.addQueryItem(QUrl::toPercentEncoding(param.first), QUrl::toPercentEncoding(param.second)); } QUrl stream_url(server_url()); diff --git a/src/subsonic/subsonicurlhandler.h b/src/subsonic/subsonicurlhandler.h index 597c66efe..387a18bc5 100644 --- a/src/subsonic/subsonicurlhandler.h +++ b/src/subsonic/subsonicurlhandler.h @@ -52,9 +52,6 @@ class SubsonicUrlHandler : public UrlHandler { typedef QPair Param; typedef QList ParamList; - typedef QPair EncodedParam; - typedef QList EncodedParamList; - SubsonicService *service_; }; diff --git a/src/tidal/tidalbaserequest.cpp b/src/tidal/tidalbaserequest.cpp index bb6cb5069..55119713a 100644 --- a/src/tidal/tidalbaserequest.cpp +++ b/src/tidal/tidalbaserequest.cpp @@ -40,8 +40,6 @@ #include "tidalservice.h" #include "tidalbaserequest.h" -const char *TidalBaseRequest::kApiUrl = "https://api.tidalhifi.com/v1"; - TidalBaseRequest::TidalBaseRequest(TidalService *service, NetworkAccessManager *network, QObject *parent) : QObject(parent), 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)); } - QUrl url(kApiUrl + QString("/") + ressource_name); + QUrl url(TidalService::kApiUrl + QString("/") + ressource_name); url.setQuery(url_query); QNetworkRequest req(url); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) @@ -128,9 +126,14 @@ QByteArray TidalBaseRequest::GetReplyData(QNetworkReply *reply, const bool send_ service_->Logout(); if (!oauth() && send_login && login_attempts() < max_login_attempts() && !api_token().isEmpty() && !username().isEmpty() && !password().isEmpty()) { qLog(Error) << "Tidal:" << error; - qLog(Info) << "Tidal:" << "Attempting to 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 { Error(error); diff --git a/src/tidal/tidalbaserequest.h b/src/tidal/tidalbaserequest.h index 2a92338e8..6f1d43c11 100644 --- a/src/tidal/tidalbaserequest.h +++ b/src/tidal/tidalbaserequest.h @@ -45,6 +45,7 @@ class TidalBaseRequest : public QObject { Q_OBJECT public: + explicit TidalBaseRequest(TidalService *service, NetworkAccessManager *network, QObject *parent = nullptr); enum QueryType { QueryType_None, @@ -57,8 +58,7 @@ class TidalBaseRequest : public QObject { QueryType_StreamURL, }; - explicit TidalBaseRequest(TidalService *service, NetworkAccessManager *network, QObject *parent); - + protected: typedef QPair Param; typedef QList ParamList; @@ -93,12 +93,12 @@ class TidalBaseRequest : public QObject { virtual void set_need_login() = 0; + signals: + void RequestLogin(); + private slots: void HandleSSLErrors(const QList &ssl_errors); - protected: - static const char *kApiUrl; - private: TidalService *service_; NetworkAccessManager *network_; diff --git a/src/tidal/tidalfavoriterequest.cpp b/src/tidal/tidalfavoriterequest.cpp index a45a9ab47..50e044633 100644 --- a/src/tidal/tidalfavoriterequest.cpp +++ b/src/tidal/tidalfavoriterequest.cpp @@ -133,7 +133,7 @@ void TidalFavoriteRequest::AddFavorites(const FavoriteType type, const SongList 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); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) 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)); } - 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); QNetworkRequest req(url); #if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) diff --git a/src/tidal/tidalfavoriterequest.h b/src/tidal/tidalfavoriterequest.h index 7aeb06443..9ab4ba60e 100644 --- a/src/tidal/tidalfavoriterequest.h +++ b/src/tidal/tidalfavoriterequest.h @@ -38,7 +38,7 @@ class TidalFavoriteRequest : public TidalBaseRequest { Q_OBJECT public: - explicit TidalFavoriteRequest(TidalService *service, NetworkAccessManager *network, QObject *parent); + explicit TidalFavoriteRequest(TidalService *service, NetworkAccessManager *network, QObject *parent = nullptr); ~TidalFavoriteRequest() override; enum FavoriteType { diff --git a/src/tidal/tidalrequest.cpp b/src/tidal/tidalrequest.cpp index 9a53b67b6..dc86ba126 100644 --- a/src/tidal/tidalrequest.cpp +++ b/src/tidal/tidalrequest.cpp @@ -887,12 +887,10 @@ void TidalRequest::SongsReceived(QNetworkReply *reply, const QString &artist_id, songs << song; } - for (Song &song : songs) { + for (Song song : songs) { if (compilation) song.set_compilation_detected(true); - if (!multidisc) { - song.set_disc(0); - } - songs_ << song; + if (!multidisc) song.set_disc(0); + songs_.insert(song.song_id(), song); } 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) { 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; } @@ -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); 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_cover_requests_queue_.enqueue(request); @@ -1215,8 +1213,10 @@ void TidalRequest::AlbumCoverReceived(QNetworkReply *reply, const QString &album if (image.loadFromData(data, format)) { if (image.save(filename, format)) { while (album_covers_requests_sent_.contains(album_id)) { - Song *song = album_covers_requests_sent_.take(album_id); - song->set_art_automatic(QUrl::fromLocalFile(filename)); + const QString song_id = album_covers_requests_sent_.take(album_id); + if (songs_.contains(song_id)) { + songs_[song_id].set_art_automatic(QUrl::fromLocalFile(filename)); + } } } else { @@ -1276,9 +1276,9 @@ void TidalRequest::FinishCheck() { } else { if (songs_.isEmpty() && errors_.isEmpty()) - emit Results(query_id_, songs_, tr("Unknown error")); + emit Results(query_id_, songs_.values(), tr("Unknown error")); else - emit Results(query_id_, songs_, ErrorsToHTML(errors_)); + emit Results(query_id_, songs_.values(), ErrorsToHTML(errors_)); } } diff --git a/src/tidal/tidalrequest.h b/src/tidal/tidalrequest.h index 6035ce12c..787a7a7dc 100644 --- a/src/tidal/tidalrequest.h +++ b/src/tidal/tidalrequest.h @@ -178,7 +178,7 @@ class TidalRequest : public TidalBaseRequest { QList artist_albums_requests_pending_; QHash album_songs_requests_pending_; - QMultiMap album_covers_requests_sent_; + QMultiMap album_covers_requests_sent_; int artists_requests_active_; int artists_total_; @@ -199,7 +199,7 @@ class TidalRequest : public TidalBaseRequest { int album_covers_requested_; int album_covers_received_; - SongList songs_; + QMap songs_; QStringList errors_; bool need_login_; bool no_results_; diff --git a/src/tidal/tidalservice.cpp b/src/tidal/tidalservice.cpp index 0f9877748..722666029 100644 --- a/src/tidal/tidalservice.cpp +++ b/src/tidal/tidalservice.cpp @@ -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::kOAuthRedirectUrl = "tidal://login/auth"; 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::kTimeResetLoginAttempts = 60000; @@ -113,8 +115,8 @@ TidalService::TidalService(Application *app, QObject *parent) pending_search_type_(InternetSearchView::SearchType_Artists), search_id_(0), login_sent_(false), - login_attempts_(0) - { + login_attempts_(0), + next_stream_url_request_id_(0) { 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::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::AlbumsAdded, albums_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()) { - TidalStreamURLRequest *stream_url_req = stream_url_requests_.takeFirst(); - QObject::disconnect(stream_url_req, nullptr, this, nullptr); + std::shared_ptr stream_url_req = stream_url_requests_.take(stream_url_requests_.firstKey()); + QObject::disconnect(stream_url_req.get(), nullptr, this, nullptr); stream_url_req->deleteLater(); } @@ -745,8 +749,9 @@ void TidalService::GetArtists() { ResetArtistsRequest(); - artists_request_.reset(new TidalRequest(this, url_handler_, app_, network_, TidalBaseRequest::QueryType_Artists, this)); + artists_request_ = std::make_shared(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::UpdateStatus, this, &TidalService::ArtistsUpdateStatusReceived); QObject::connect(artists_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::ArtistsProgressSetMaximumReceived); @@ -803,7 +808,8 @@ void TidalService::GetAlbums() { } ResetAlbumsRequest(); - albums_request_.reset(new TidalRequest(this, url_handler_, app_, network_, TidalBaseRequest::QueryType_Albums, this)); + albums_request_ = std::make_shared(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::UpdateStatus, this, &TidalService::AlbumsUpdateStatusReceived); QObject::connect(albums_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::AlbumsProgressSetMaximumReceived); @@ -860,7 +866,8 @@ void TidalService::GetSongs() { } ResetSongsRequest(); - songs_request_.reset(new TidalRequest(this, url_handler_, app_, network_, TidalBaseRequest::QueryType_Songs, this)); + songs_request_ = std::make_shared(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::UpdateStatus, this, &TidalService::SongsUpdateStatusReceived); QObject::connect(songs_request_.get(), &TidalRequest::ProgressSetMaximum, this, &TidalService::SongsProgressSetMaximumReceived); @@ -953,8 +960,9 @@ void TidalService::SendSearch() { return; } - search_request_.reset(new TidalRequest(this, url_handler_, app_, network_, type, this)); + search_request_ = std::make_shared(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::UpdateStatus, this, &TidalService::SearchUpdateStatus); 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); - stream_url_requests_ << stream_url_req; + const int id = ++next_stream_url_request_id_; + std::shared_ptr stream_url_req = std::make_shared(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, &TidalStreamURLRequest::StreamURLFinished, this, &TidalService::HandleStreamURLFinished); - QObject::connect(this, &TidalService::LoginComplete, stream_url_req, &TidalStreamURLRequest::LoginComplete); + QObject::connect(stream_url_req.get(), &TidalStreamURLRequest::TryLogin, this, &TidalService::TryLogin); + QObject::connect(stream_url_req.get(), &TidalStreamURLRequest::StreamURLFinished, this, &TidalService::HandleStreamURLFinished); + QObject::connect(this, &TidalService::LoginComplete, stream_url_req.get(), &TidalStreamURLRequest::LoginComplete); 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(sender()); - if (!stream_url_req || !stream_url_requests_.contains(stream_url_req)) return; - stream_url_req->deleteLater(); - stream_url_requests_.removeAll(stream_url_req); + if (!stream_url_requests_.contains(id)) return; + std::shared_ptr stream_url_req = stream_url_requests_.take(id); emit StreamURLFinished(original_url, stream_url, filetype, samplerate, bit_depth, duration, error); diff --git a/src/tidal/tidalservice.h b/src/tidal/tidalservice.h index f5d354808..ec1f4a28b 100644 --- a/src/tidal/tidalservice.h +++ b/src/tidal/tidalservice.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,8 @@ class TidalService : public InternetService { ~TidalService() override; static const Song::Source kSource; + static const char *kApiUrl; + static const char *kResourcesUrl; void Exit() override; void ReloadSettings() override; @@ -113,15 +116,6 @@ class TidalService : public InternetService { QSortFilterProxyModel *albums_collection_sort_model() override { return albums_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: void ShowConfig() override; void StartAuthorization(const QString &client_id); @@ -157,7 +151,7 @@ class TidalService : public InternetService { void ArtistsUpdateProgressReceived(const int id, const int progress); void AlbumsUpdateProgressReceived(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: typedef QPair Param; @@ -172,6 +166,7 @@ class TidalService : public InternetService { static const char *kOAuthAccessTokenUrl; static const char *kOAuthRedirectUrl; static const char *kAuthUrl; + static const int kLoginAttempts; static const int kTimeResetLoginAttempts; @@ -246,7 +241,8 @@ class TidalService : public InternetService { QString code_verifier_; QString code_challenge_; - QList stream_url_requests_; + int next_stream_url_request_id_; + QMap> stream_url_requests_; QStringList login_errors_; diff --git a/src/tidal/tidalstreamurlrequest.cpp b/src/tidal/tidalstreamurlrequest.cpp index f77083d7f..1a8e66f3c 100644 --- a/src/tidal/tidalstreamurlrequest.cpp +++ b/src/tidal/tidalstreamurlrequest.cpp @@ -46,11 +46,12 @@ #include "tidalbaserequest.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), service_(service), reply_(nullptr), original_url_(original_url), + id_(id), song_id_(original_url.path().toInt()), tries_(0), need_login_(false) {} @@ -71,7 +72,7 @@ void TidalStreamURLRequest::LoginComplete(const bool success, const QString &err need_login_ = false; 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; } @@ -83,11 +84,11 @@ void TidalStreamURLRequest::Process() { if (!authenticated()) { 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; } 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; } need_login_ = true; @@ -105,7 +106,7 @@ void TidalStreamURLRequest::Cancel() { reply_->abort(); } 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; 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; } reply_ = nullptr; @@ -169,19 +170,19 @@ void TidalStreamURLRequest::StreamURLReceived() { QJsonObject json_obj = ExtractJsonObj(data); 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; } if (!json_obj.contains("trackId")) { 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; } int track_id(json_obj["trackId"].toInt()); if (track_id != song_id_) { 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; } @@ -214,7 +215,7 @@ void TidalStreamURLRequest::StreamURLReceived() { QString filename = "tidal-" + QString::number(song_id_) + ".xml"; if (!QDir().mkpath(filepath)) { 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; } QUrl url("file://" + filepath + "/" + filename); @@ -223,7 +224,7 @@ void TidalStreamURLRequest::StreamURLReceived() { file.remove(); if (!file.open(QIODevice::WriteOnly)) { 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; } file.write(data_manifest); @@ -237,13 +238,13 @@ void TidalStreamURLRequest::StreamURLReceived() { json_obj = ExtractJsonObj(data_manifest); 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; } if (!json_obj.contains("mimeType")) { 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; } @@ -266,7 +267,7 @@ void TidalStreamURLRequest::StreamURLReceived() { QJsonValue json_urls = json_obj["urls"]; if (!json_urls.isArray()) { 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; } QJsonArray json_array_urls = json_urls.toArray(); @@ -287,11 +288,11 @@ void TidalStreamURLRequest::StreamURLReceived() { if (urls.isEmpty()) { 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; } - emit StreamURLFinished(original_url_, urls.first(), filetype, -1, -1, -1); + emit StreamURLFinished(id_, original_url_, urls.first(), filetype, -1, -1, -1); } diff --git a/src/tidal/tidalstreamurlrequest.h b/src/tidal/tidalstreamurlrequest.h index c9c41e0aa..3f260ed64 100644 --- a/src/tidal/tidalstreamurlrequest.h +++ b/src/tidal/tidalstreamurlrequest.h @@ -41,7 +41,7 @@ class TidalStreamURLRequest : public TidalBaseRequest { Q_OBJECT 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; void GetStreamURL(); @@ -58,7 +58,7 @@ class TidalStreamURLRequest : public TidalBaseRequest { signals: 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: void StreamURLReceived(); @@ -72,6 +72,7 @@ class TidalStreamURLRequest : public TidalBaseRequest { TidalService *service_; QNetworkReply *reply_; QUrl original_url_; + int id_; int song_id_; int tries_; bool need_login_;