diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 889ed0b9..c552865e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -192,8 +192,14 @@ set(SOURCES covermanager/coverfromurldialog.cpp covermanager/musicbrainzcoverprovider.cpp covermanager/discogscoverprovider.cpp - #covermanager/amazoncoverprovider.cpp - + + lyrics/lyricsproviders.cpp + lyrics/lyricsprovider.cpp + lyrics/lyricsfetcher.cpp + lyrics/lyricsfetchersearch.cpp + lyrics/auddlyricsprovider.cpp + lyrics/apiseedslyricsprovider.cpp + settings/settingsdialog.cpp settings/settingspage.cpp settings/behavioursettingspage.cpp @@ -258,13 +264,7 @@ set(SOURCES tidal/tidalsearchmodel.cpp tidal/tidalsearchsortmodel.cpp tidal/tidalsearchitemdelegate.cpp - - lyrics/lyricsproviders.cpp - lyrics/lyricsprovider.cpp - lyrics/lyricsfetcher.cpp - lyrics/lyricsfetchersearch.cpp - lyrics/auddlyricsprovider.cpp - lyrics/apiseedslyricsprovider.cpp + tidal/tidalurlhandler.cpp ) @@ -359,7 +359,13 @@ set(HEADERS covermanager/coverfromurldialog.h covermanager/musicbrainzcoverprovider.h covermanager/discogscoverprovider.h - #covermanager/amazoncoverprovider.h + + lyrics/lyricsproviders.h + lyrics/lyricsprovider.h + lyrics/lyricsfetcher.h + lyrics/lyricsfetchersearch.h + lyrics/auddlyricsprovider.h + lyrics/apiseedslyricsprovider.h settings/settingsdialog.h settings/settingspage.h @@ -421,13 +427,7 @@ set(HEADERS tidal/tidalsearch.h tidal/tidalsearchview.h tidal/tidalsearchmodel.h - - lyrics/lyricsproviders.h - lyrics/lyricsprovider.h - lyrics/lyricsfetcher.h - lyrics/lyricsfetchersearch.h - lyrics/auddlyricsprovider.h - lyrics/apiseedslyricsprovider.h + tidal/tidalurlhandler.h ) diff --git a/src/context/contextview.cpp b/src/context/contextview.cpp index 17719104..22eafe04 100644 --- a/src/context/contextview.cpp +++ b/src/context/contextview.cpp @@ -552,6 +552,7 @@ void ContextView::ScaleCover() { void ContextView::AlbumArtLoaded(const Song &song, const QString&, const QImage &image) { if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return; + if (image == image_original_) return; active_ = true; downloading_covers_ = false; diff --git a/src/core/player.cpp b/src/core/player.cpp index e092e4ea..e09e82af 100644 --- a/src/core/player.cpp +++ b/src/core/player.cpp @@ -224,10 +224,26 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) { qLog(Debug) << "URL handler for" << result.original_url_ << "returned" << result.media_url_; + Song song = item->Metadata(); + bool update(false); + + // If there was no filetype in the song's metadata, use the one provided by URL handler, if there is one + if ( + (item->Metadata().filetype() == Song::FileType_Unknown && result.filetype_ != Song::FileType_Unknown) + || + (item->Metadata().filetype() == Song::FileType_Stream && result.filetype_ != Song::FileType_Stream) + ) + { + song.set_filetype(result.filetype_); + update = true; + } // If there was no length info in song's metadata, use the one provided by URL handler, if there is one - if (item->Metadata().length_nanosec() <= 0 && result.length_nanosec_ != -1) { + if (item->Metadata().length_nanosec() <= 0 && result.length_nanosec_ != -1) { Song song = item->Metadata(); song.set_length_nanosec(result.length_nanosec_); + update = true; + } + if (update) { item->SetTemporaryMetadata(song); app_->playlist_manager()->active()->InformOfCurrentSongChange(); } diff --git a/src/core/urlhandler.cpp b/src/core/urlhandler.cpp index c0031d10..aaa2248b 100644 --- a/src/core/urlhandler.cpp +++ b/src/core/urlhandler.cpp @@ -27,7 +27,7 @@ #include "urlhandler.h" -UrlHandler::LoadResult::LoadResult(const QUrl &original_url, Type type, const QUrl &media_url, qint64 length_nanosec) : original_url_(original_url), type_(type), media_url_(media_url), length_nanosec_(length_nanosec) {} +UrlHandler::LoadResult::LoadResult(const QUrl &original_url, Type type, const QUrl &media_url, const Song::FileType &filetype, qint64 length_nanosec) : original_url_(original_url), type_(type), media_url_(media_url), filetype_(filetype), length_nanosec_(length_nanosec) {} UrlHandler::UrlHandler(QObject *parent) : QObject(parent) {} diff --git a/src/core/urlhandler.h b/src/core/urlhandler.h index e4294e11..205738c8 100644 --- a/src/core/urlhandler.h +++ b/src/core/urlhandler.h @@ -29,6 +29,8 @@ #include #include +#include "song.h" + class UrlHandler : public QObject { Q_OBJECT @@ -53,7 +55,7 @@ class UrlHandler : public QObject { TrackAvailable, }; - LoadResult(const QUrl &original_url = QUrl(), Type type = NoMoreTracks, const QUrl &media_url = QUrl(), qint64 length_nanosec_ = -1); + LoadResult(const QUrl &original_url = QUrl(), Type type = NoMoreTracks, const QUrl &media_url = QUrl(), const Song::FileType &filetype = Song::FileType_Stream, qint64 length_nanosec_ = -1); // The url that the playlist item has in Url(). // Might be something unplayable like lastfm://... @@ -64,6 +66,9 @@ class UrlHandler : public QObject { // The actual url to something that gstreamer can play. QUrl media_url_; + // The type of the stream + Song::FileType filetype_; + // Track length, if we are able to get it only now qint64 length_nanosec_; }; @@ -78,8 +83,9 @@ class UrlHandler : public QObject { virtual void TrackAboutToEnd() {}; virtual void TrackSkipped() {}; -signals: + signals: void AsyncLoadComplete(const UrlHandler::LoadResult &result); + }; #endif // URLHANDLER_H diff --git a/src/internet/internetmodel.h b/src/internet/internetmodel.h index 9e650061..0bf045fb 100644 --- a/src/internet/internetmodel.h +++ b/src/internet/internetmodel.h @@ -110,7 +110,6 @@ class InternetModel : public QStandardItemModel { template static T *Service() { - //return static_cast(ServiceByName(T::kServiceName)); return static_cast(ServiceBySource(T::kSource)); } diff --git a/src/internet/internetservice.cpp b/src/internet/internetservice.cpp index c0a6572c..4b4c4938 100644 --- a/src/internet/internetservice.cpp +++ b/src/internet/internetservice.cpp @@ -27,6 +27,6 @@ #include "internetmodel.h" #include "internetservice.h" -InternetService::InternetService(Song::Source source, const QString &name, Application *app, InternetModel *model, QObject *parent) - : QObject(parent), app_(app), model_(model), source_(source), name_(name) { +InternetService::InternetService(Song::Source source, const QString &name, const QString &url_scheme, Application *app, InternetModel *model, QObject *parent) + : QObject(parent), app_(app), model_(model), source_(source), name_(name), url_scheme_(url_scheme) { } diff --git a/src/internet/internetservice.h b/src/internet/internetservice.h index 6ac95d01..71f23a9f 100644 --- a/src/internet/internetservice.h +++ b/src/internet/internetservice.h @@ -42,10 +42,11 @@ class InternetService : public QObject { Q_OBJECT public: - InternetService(Song::Source source, const QString &name, Application *app, InternetModel *model, QObject *parent = nullptr); + InternetService(Song::Source source, const QString &name, const QString &url_scheme, Application *app, InternetModel *model, QObject *parent = nullptr); virtual ~InternetService() {} Song::Source source() const { return source_; } QString name() const { return name_; } + QString url_scheme() const { return url_scheme_; } InternetModel *model() const { return model_; } virtual bool has_initial_load_settings() const { return false; } virtual void InitialLoadSettings() {} @@ -61,6 +62,7 @@ class InternetService : public QObject { InternetModel *model_; Song::Source source_; QString name_; + QString url_scheme_; }; Q_DECLARE_METATYPE(InternetService*); diff --git a/src/settings/tidalsettingspage.cpp b/src/settings/tidalsettingspage.cpp index b6fd4ca2..1eb874d3 100644 --- a/src/settings/tidalsettingspage.cpp +++ b/src/settings/tidalsettingspage.cpp @@ -45,7 +45,7 @@ TidalSettingsPage::TidalSettingsPage(SettingsDialog *parent) connect(ui_->button_login, SIGNAL(clicked()), SLOT(LoginClicked())); connect(ui_->login_state, SIGNAL(LogoutClicked()), SLOT(LogoutClicked())); - connect(this, SIGNAL(Login(QString, QString, int)), service_, SLOT(SendLogin(QString, QString, int))); + connect(this, SIGNAL(Login(QString, QString)), service_, SLOT(SendLogin(QString, QString))); connect(service_, SIGNAL(LoginFailure(QString)), SLOT(LoginFailure(QString))); connect(service_, SIGNAL(LoginSuccess()), SLOT(LoginSuccess())); diff --git a/src/tidal/tidalservice.cpp b/src/tidal/tidalservice.cpp index 76f0e832..c39363bc 100644 --- a/src/tidal/tidalservice.cpp +++ b/src/tidal/tidalservice.cpp @@ -39,6 +39,7 @@ #include #include "core/application.h" +#include "core/player.h" #include "core/closure.h" #include "core/logging.h" #include "core/mergedproxymodel.h" @@ -51,20 +52,22 @@ #include "internet/internetmodel.h" #include "tidalservice.h" #include "tidalsearch.h" +#include "tidalurlhandler.h" #include "settings/tidalsettingspage.h" const Song::Source TidalService::kSource = Song::Source_Tidal; -const char *TidalService::kServiceName = "Tidal"; const char *TidalService::kApiUrl = "https://listen.tidal.com/v1"; const char *TidalService::kAuthUrl = "https://listen.tidal.com/v1/login/username"; const char *TidalService::kResourcesUrl = "http://resources.tidal.com"; const char *TidalService::kApiToken = "P5Xbeo5LFvESeDy6"; +const int TidalService::kLoginAttempts = 2; typedef QPair Param; TidalService::TidalService(Application *app, InternetModel *parent) - : InternetService(kSource, kServiceName, app, parent, parent), + : InternetService(Song::Source_Tidal, "Tidal", "tidal", app, parent, parent), network_(new NetworkAccessManager(this)), + url_handler_(new TidalUrlHandler(app, this)), timer_searchdelay_(new QTimer(this)), searchdelay_(1500), albumssearchlimit_(1), @@ -79,8 +82,10 @@ TidalService::TidalService(Application *app, InternetModel *parent) timer_searchdelay_->setSingleShot(true); connect(timer_searchdelay_, SIGNAL(timeout()), SLOT(StartSearch())); - connect(this, SIGNAL(Login(int)), SLOT(SendLogin(int))); - connect(this, SIGNAL(Login(QString, QString, int)), SLOT(SendLogin(QString, QString, int))); + connect(this, SIGNAL(Login()), SLOT(SendLogin())); + connect(this, SIGNAL(Login(QString, QString)), SLOT(SendLogin(QString, QString))); + + app->player()->RegisterUrlHandler(url_handler_); ReloadSettings(); LoadSessionID(); @@ -121,13 +126,13 @@ void TidalService::LoadSessionID() { } -void TidalService::SendLogin(const int search_id) { - SendLogin(username_, password_, search_id); +void TidalService::SendLogin() { + SendLogin(username_, password_); } -void TidalService::SendLogin(const QString &username, const QString &password, const int search_id) { +void TidalService::SendLogin(const QString &username, const QString &password) { - if (search_id != 0) emit UpdateStatus("Authenticating..."); + if (search_id_ != 0) emit UpdateStatus("Authenticating..."); login_sent_ = true; login_attempts_++; @@ -153,11 +158,11 @@ void TidalService::SendLogin(const QString &username, const QString &password, c req.setRawHeader("Origin", "http://listen.tidal.com"); QNetworkReply *reply = network_->post(req, url_query.toString(QUrl::FullyEncoded).toUtf8()); - NewClosure(reply, SIGNAL(finished()), this, SLOT(HandleAuthReply(QNetworkReply*, int)), reply, search_id); + NewClosure(reply, SIGNAL(finished()), this, SLOT(HandleAuthReply(QNetworkReply*)), reply); } -void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) { +void TidalService::HandleAuthReply(QNetworkReply *reply) { reply->deleteLater(); @@ -167,7 +172,7 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) { if (reply->error() < 200) { // This is a network error, there is nothing more to do. QString failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); - if (search_id != 0) Error(failure_reason); + Error(failure_reason); emit LoginFailure(failure_reason); return; } @@ -189,7 +194,7 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) { else { failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); } - if (search_id != 0) Error(failure_reason); + Error(failure_reason); emit LoginFailure(failure_reason); return; } @@ -201,21 +206,21 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) { if (error.error != QJsonParseError::NoError) { QString failure_reason("Authentication reply from server missing Json data."); - if (search_id != 0) Error(failure_reason); + Error(failure_reason); emit LoginFailure(failure_reason); return; } if (json_doc.isNull() || json_doc.isEmpty()) { QString failure_reason("Authentication reply from server has empty Json document."); - if (search_id != 0) Error(failure_reason); + Error(failure_reason); emit LoginFailure(failure_reason); return; } if (!json_doc.isObject()) { QString failure_reason("Authentication reply from server has Json document that is not an object."); - if (search_id != 0) Error(failure_reason); + Error(failure_reason); emit LoginFailure(failure_reason); return; } @@ -223,14 +228,14 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) { QJsonObject json_obj = json_doc.object(); if (json_obj.isEmpty()) { QString failure_reason("Authentication reply from server has empty Json object."); - if (search_id != 0) Error(failure_reason); + Error(failure_reason); emit LoginFailure(failure_reason); return; } if ( !json_obj.contains("userId") || !json_obj.contains("sessionId") || !json_obj.contains("countryCode") ) { QString failure_reason("Authentication reply from server is missing userId, sessionId or countryCode"); - if (search_id != 0) Error(failure_reason); + Error(failure_reason); emit LoginFailure(failure_reason); return; } @@ -248,10 +253,16 @@ void TidalService::HandleAuthReply(QNetworkReply *reply, int search_id) { qLog(Debug) << "Tidal: Login successful" << "user id" << user_id_ << "session id" << session_id_ << "country code" << country_code_; - if (search_id != 0) { - qLog(Debug) << "Tidal: Resuming search"; + login_attempts_ = 0; + + if (search_id_ != 0) { + qLog(Debug) << "Tidal: Resuming search" << search_id_; SendSearch(); } + if (!stream_request_url_.isEmpty()) { + qLog(Debug) << "Tidal: Resuming get stream url" << stream_request_url_; + emit GetStreamURL(stream_request_url_); + } emit LoginSuccess(); @@ -303,7 +314,7 @@ QNetworkReply *TidalService::CreateRequest(const QString &ressource_name, const } -QJsonObject TidalService::ExtractJsonObj(QNetworkReply *reply, bool sendlogin) { +QJsonObject TidalService::ExtractJsonObj(QNetworkReply *reply, const bool sendlogin) { QByteArray data; @@ -334,14 +345,15 @@ QJsonObject TidalService::ExtractJsonObj(QNetworkReply *reply, bool sendlogin) { else { failure_reason = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); } + qLog(Debug) << reply->error(); if (reply->error() == QNetworkReply::ContentAccessDenied || reply->error() == QNetworkReply::ContentOperationNotPermittedError || reply->error() == QNetworkReply::AuthenticationRequiredError) { // Session is probably expired, attempt to login once Logout(); - if (sendlogin && login_attempts_ < 1 && !username_.isEmpty() && !password_.isEmpty()) { + if (sendlogin && login_attempts_ < kLoginAttempts && !username_.isEmpty() && !password_.isEmpty()) { qLog(Error) << "Tidal:" << failure_reason; qLog(Error) << "Tidal:" << QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); qLog(Error) << "Tidal:" << "Attempting to login."; - emit Login(search_id_); + emit Login(); } else { Error(failure_reason); @@ -439,7 +451,7 @@ void TidalService::StartSearch() { search_text_ = pending_search_text_; if (authenticated()) SendSearch(); - else emit Login(username_, password_, search_id_); + else emit Login(username_, password_); } @@ -525,9 +537,10 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) { // This was a tracks search if (!fetchalbums_) { Song song = ParseSong(0, value); - requests_song_.insert(song.id(), song); - songs_requested_++; - GetStreamURL(0, song.id()); + //requests_song_.insert(song.id(), song); + //songs_requested_++; + //GetStreamURL(0, song.id()); + songs_ << song; continue; } QJsonValue json_value_album = json_obj["album"]; @@ -649,9 +662,10 @@ void TidalService::GetAlbumFinished(QNetworkReply *reply, int search_id, int alb QString album_full(QString("%1 - (Disc %2)").arg(song.album()).arg(song.disc())); song.set_album(album_full); } - requests_song_.insert(song.id(), song); - songs_requested_++; - GetStreamURL(album_id, song.id()); + //requests_song_.insert(song.id(), song); + //songs_requested_++; + //GetStreamURL(album_id, song.id()); + songs_ << song; } if (albums_requested_ <= albums_received_) { @@ -702,10 +716,11 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val QJsonArray json_artists = json_obj["artists"].toArray(); int song_id = json_obj["id"].toInt(); + if (requests_song_.contains(song_id)) return requests_song_.value(song_id); QString title = json_obj["title"].toString(); - QString url = json_obj["url"].toString(); + QString urlstr = json_obj["url"].toString(); int track = json_obj["trackNumber"].toInt(); int disc = json_obj["volumeNumber"].toInt(); bool allow_streaming = json_obj["allowStreaming"].toBool(); @@ -760,9 +775,6 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val song.set_title(title); song.set_track(track); song.set_disc(disc); - song.set_bitrate(0); - song.set_samplerate(0); - song.set_bitdepth(0); QVariant q_duration = json_duration.toVariant(); if (q_duration.isValid()) { @@ -773,13 +785,23 @@ Song TidalService::ParseSong(const int album_id_requested, const QJsonValue &val cover = cover.replace("-", "/"); QUrl cover_url (QString("%1/images/%2/%3.jpg").arg(kResourcesUrl).arg(cover).arg(coversize_)); song.set_art_automatic(cover_url.toEncoded()); + + QUrl url; + url.setScheme(url_handler_->scheme()); + url.setPath(QString::number(song_id)); + song.set_url(url); + song.set_valid(true); return song; } -void TidalService::GetStreamURL(const int album_id, const int song_id) { +void TidalService::GetStreamURL(const QUrl &url) { + + stream_request_url_ = url; + int song_id = url.path().toInt(); + requests_song_.insert(song_id, url); QList parameters; parameters << Param("token", session_id_) @@ -787,60 +809,44 @@ void TidalService::GetStreamURL(const int album_id, const int song_id) { QNetworkReply *reply = CreateRequest(QString("tracks/%1/streamUrl").arg(song_id), parameters); - NewClosure(reply, SIGNAL(finished()), this, SLOT(GetStreamURLFinished(QNetworkReply*, int, int)), reply, search_id_, song_id); + NewClosure(reply, SIGNAL(finished()), this, SLOT(GetStreamURLFinished(QNetworkReply*, int, QUrl)), reply, song_id, url); } -void TidalService::GetStreamURLFinished(QNetworkReply *reply, const int search_id, const int song_id) { +void TidalService::GetStreamURLFinished(QNetworkReply *reply, const int song_id, const QUrl original_url) { reply->deleteLater(); + if (requests_song_.contains(song_id)) requests_song_.remove(song_id); + if (original_url != stream_request_url_) return; - if (search_id != search_id_) return; - - if (!requests_song_.contains(song_id)) { - CheckFinish(); - return; - } - Song song = requests_song_.value(song_id); - songs_received_++; - - if (albums_requested_ <= albums_received_) { - emit UpdateProgress(songs_received_); - } - - QJsonObject json_obj = ExtractJsonObj(reply); + QJsonObject json_obj = ExtractJsonObj(reply, true); if (json_obj.isEmpty()) { - requests_song_.remove(song_id); - CheckFinish(); + if (!stream_request_url_.isEmpty() && !login_sent_) { + emit StreamURLFinished(QUrl(), Song::FileType_Stream); + stream_request_url_ = QUrl(); + } return; } if (!json_obj.contains("url") || !json_obj.contains("codec")) { qLog(Error) << "Tidal: Invalid Json reply, stream missing url or codec."; qLog(Debug) << json_obj; - requests_song_.remove(song_id); - CheckFinish(); + emit StreamURLFinished(QUrl(), Song::FileType_Stream); + stream_request_url_ = QUrl(); return; } - song.set_url(QUrl(json_obj["url"].toString())); + stream_request_url_ = QUrl(); - QString codec = json_obj["codec"].toString().toLower(); - song.set_filetype(Song::FiletypeByExtension(codec)); - if (song.filetype() == Song::FileType_Unknown) { + QUrl new_url(json_obj["url"].toString()); + QString codec(json_obj["codec"].toString().toLower()); + Song::FileType filetype(Song::FiletypeByExtension(codec)); + if (filetype == Song::FileType_Unknown) { qLog(Debug) << "Tidal: Unknown codec" << codec; - song.set_filetype(Song::FileType_Stream); + filetype = Song::FileType_Stream; } - song.set_valid(true); - - //qLog(Debug) << song.artist() << song.album() << song.title() << song.url() << song.filetype(); - - songs_ << song; - - requests_song_.remove(song_id); - - CheckFinish(); + emit StreamURLFinished(new_url, filetype); } @@ -862,6 +868,15 @@ void TidalService::CheckFinish() { void TidalService::Error(QString error, QString debug) { qLog(Error) << "Tidal:" << error; if (!debug.isEmpty()) qLog(Debug) << debug; - search_error_ = error; - CheckFinish(); + if (search_id_ != 0) { + if (!error.isEmpty()) { + search_error_ += error; + search_error_ += "
"; + } + CheckFinish(); + } + if (!stream_request_url_.isEmpty() && !login_sent_) { + emit StreamURLFinished(QUrl(), Song::FileType_Stream); + stream_request_url_ = QUrl(); + } } diff --git a/src/tidal/tidalservice.h b/src/tidal/tidalservice.h index 95f1b949..5b7cee1c 100644 --- a/src/tidal/tidalservice.h +++ b/src/tidal/tidalservice.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include "settings/tidalsettingspage.h" class NetworkAccessManager; +class TidalUrlHandler; class TidalService : public InternetService { Q_OBJECT @@ -48,7 +50,7 @@ class TidalService : public InternetService { ~TidalService(); static const Song::Source kSource; - static const char *kServiceName; + static const int kLoginAttempts; void ReloadSettings(); @@ -59,9 +61,11 @@ class TidalService : public InternetService { const bool login_sent() { return login_sent_; } const bool authenticated() { return (!session_id_.isEmpty() && !country_code_.isEmpty()); } + void GetStreamURL(const QUrl &url); + signals: - void Login(const int search_id = 0); - void Login(const QString &username, const QString &password, const int search_id = 0); + void Login(); + void Login(const QString &username, const QString &password); void LoginSuccess(); void LoginFailure(QString failure_reason); void SearchResults(int id, SongList songs); @@ -69,18 +73,20 @@ class TidalService : public InternetService { void UpdateStatus(QString text); void ProgressSetMaximum(int max); void UpdateProgress(int max); + void GetStreamURLFinished(QNetworkReply *reply, const QUrl url); + void StreamURLFinished(const QUrl url, const Song::FileType); public slots: void ShowConfig(); - void SendLogin(const QString &username, const QString &password, const int search_id = 0); + void SendLogin(const QString &username, const QString &password); private slots: - void SendLogin(const int search_id = 0); - void HandleAuthReply(QNetworkReply *reply, int search_id); + void SendLogin(); + void HandleAuthReply(QNetworkReply *reply); void StartSearch(); void SearchFinished(QNetworkReply *reply, int search_id); void GetAlbumFinished(QNetworkReply *reply, int search_id, int album_id); - void GetStreamURLFinished(QNetworkReply *reply, const int search_id, const int song_id); + void GetStreamURLFinished(QNetworkReply *reply, const int song_id, const QUrl original_url); private: void ClearSearch(); @@ -91,7 +97,6 @@ class TidalService : public InternetService { void SendSearch(); void GetAlbum(const int album_id); Song ParseSong(const int album_id_requested, const QJsonValue &value); - void GetStreamURL(const int album_id, const int song_id); void CheckFinish(); void Error(QString error, QString debug = QString()); @@ -101,6 +106,7 @@ class TidalService : public InternetService { static const char *kApiToken; NetworkAccessManager *network_; + TidalUrlHandler *url_handler_; QTimer *timer_searchdelay_; QString username_; @@ -123,7 +129,7 @@ class TidalService : public InternetService { int search_id_; QString search_text_; QHash requests_album_; - QHash requests_song_; + QHash requests_song_; int albums_requested_; int albums_received_; int songs_requested_; @@ -132,6 +138,7 @@ class TidalService : public InternetService { QString search_error_; bool login_sent_; int login_attempts_; + QUrl stream_request_url_; }; diff --git a/src/widgets/playingwidget.cpp b/src/widgets/playingwidget.cpp index d38a8542..dee26008 100644 --- a/src/widgets/playingwidget.cpp +++ b/src/widgets/playingwidget.cpp @@ -274,8 +274,8 @@ void PlayingWidget::SongChanged(const Song &song) { void PlayingWidget::AlbumArtLoaded(const Song &song, const QString &, const QImage &image) { if (!playing_) return; - if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return; + if (timeline_fade_->state() == QTimeLine::Running && image == image_original_ && song.effective_albumartist() == song_.effective_albumartist() && song.effective_album() == song_.effective_album() && song.title() == song_.title()) return; active_ = true; downloading_covers_ = false;