From 63d5018ad6662cae7343128a64fecb0cd707c923 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Wed, 17 Apr 2019 22:18:03 +0200 Subject: [PATCH] Improve cover providers score system --- src/covermanager/albumcoverfetcher.cpp | 4 +- src/covermanager/albumcoverfetcher.h | 14 ++- src/covermanager/albumcoverfetchersearch.cpp | 44 ++++++--- src/covermanager/albumcoverfetchersearch.h | 6 +- src/covermanager/albumcovermanager.cpp | 1 + src/covermanager/albumcoversearcher.cpp | 2 +- src/covermanager/coverprovider.cpp | 4 +- src/covermanager/coverprovider.h | 4 +- src/covermanager/deezercoverprovider.cpp | 8 +- src/covermanager/discogscoverprovider.cpp | 93 +++++++++++++++---- src/covermanager/discogscoverprovider.h | 3 +- src/covermanager/lastfmcoverprovider.cpp | 9 +- src/covermanager/musicbrainzcoverprovider.cpp | 54 +++++++++-- src/covermanager/tidalcoverprovider.cpp | 10 +- 14 files changed, 190 insertions(+), 66 deletions(-) diff --git a/src/covermanager/albumcoverfetcher.cpp b/src/covermanager/albumcoverfetcher.cpp index 9e6a7bd9d..e79a39b11 100644 --- a/src/covermanager/albumcoverfetcher.cpp +++ b/src/covermanager/albumcoverfetcher.cpp @@ -48,12 +48,12 @@ AlbumCoverFetcher::AlbumCoverFetcher(CoverProviders *cover_providers, QObject *p quint64 AlbumCoverFetcher::FetchAlbumCover(const QString &artist, const QString &album, bool fetchall) { CoverSearchRequest request; + request.id = next_id_++; request.artist = artist; request.album = album; request.album.remove(Song::kAlbumRemoveDisc); request.album.remove(Song::kAlbumRemoveMisc); request.search = false; - request.id = next_id_++; request.fetchall = fetchall; AddRequest(request); @@ -64,12 +64,12 @@ quint64 AlbumCoverFetcher::FetchAlbumCover(const QString &artist, const QString quint64 AlbumCoverFetcher::SearchForCovers(const QString &artist, const QString &album) { CoverSearchRequest request; + request.id = next_id_++; request.artist = artist; request.album = album; request.album.remove(Song::kAlbumRemoveDisc); request.album.remove(Song::kAlbumRemoveMisc); request.search = true; - request.id = next_id_++; request.fetchall = false; AddRequest(request); diff --git a/src/covermanager/albumcoverfetcher.h b/src/covermanager/albumcoverfetcher.h index e28a64dbf..08cf190f0 100644 --- a/src/covermanager/albumcoverfetcher.h +++ b/src/covermanager/albumcoverfetcher.h @@ -60,16 +60,20 @@ struct CoverSearchRequest { }; // This structure represents a single result of some album's cover search request. -// It contains an URL that leads to a found cover plus its description (usually the "artist - album" string). struct CoverSearchResult { - // Used for grouping in the user interface. This is set automatically - don't set it manually in your cover provider. + // Used for grouping in the user interface. QString provider; - // Description of this result (we suggest using the "artist - album" format) - QString description; + // Artist and album returned by the provider + QString artist; + QString album; - // An URL of a cover image described by this CoverSearchResult + // An URL of a cover image QUrl image_url; + + // Total score for this result + float score; + }; Q_DECLARE_METATYPE(CoverSearchResult); diff --git a/src/covermanager/albumcoverfetchersearch.cpp b/src/covermanager/albumcoverfetchersearch.cpp index 923cdc16d..c7bb7aba4 100644 --- a/src/covermanager/albumcoverfetchersearch.cpp +++ b/src/covermanager/albumcoverfetchersearch.cpp @@ -43,12 +43,15 @@ #include "coverprovider.h" #include "coverproviders.h" +using std::min; +using std::max; using std::stable_sort; +using std::sqrt; const int AlbumCoverFetcherSearch::kSearchTimeoutMs = 25000; const int AlbumCoverFetcherSearch::kImageLoadTimeoutMs = 3000; const int AlbumCoverFetcherSearch::kTargetSize = 500; -const float AlbumCoverFetcherSearch::kGoodScore = 1.85; +const float AlbumCoverFetcherSearch::kGoodScore = 4; AlbumCoverFetcherSearch::AlbumCoverFetcherSearch( const CoverSearchRequest &request, QNetworkAccessManager *network, QObject *parent) @@ -107,13 +110,19 @@ static bool CompareProviders(const CoverSearchResult &a, const CoverSearchResult void AlbumCoverFetcherSearch::ProviderSearchFinished(int id, const QList &results) { if (!pending_requests_.contains(id)) return; - CoverProvider *provider = pending_requests_.take(id); CoverSearchResults results_copy(results); - // Set categories on the results for (int i = 0; i < results_copy.count(); ++i) { results_copy[i].provider = provider->name(); + results_copy[i].score = provider->quality(); + if (results_copy[i].artist == request_.artist) { + results_copy[i].score += 0.5; + } + if (results_copy[i].album == request_.album) { results_copy[i].score += 0.5; } + if (results_copy[i].artist != request_.artist && results_copy[i].album != request_.album) { + results_copy[i].score -= 1.0; + } } // Add results from the current provider to our pool @@ -126,6 +135,7 @@ void AlbumCoverFetcherSearch::ProviderSearchFinished(int id, const QListget(QNetworkRequest(result.image_url))); NewClosure(image_reply, SIGNAL(finished()), this, SLOT(ProviderCoverFetchFinished(RedirectFollower*)), image_reply); - pending_image_loads_[image_reply] = result.provider; + pending_image_loads_[image_reply] = result; image_load_timeout_->AddReply(image_reply); statistics_.network_requests_made_++; @@ -187,7 +197,9 @@ void AlbumCoverFetcherSearch::FetchMoreImages() { void AlbumCoverFetcherSearch::ProviderCoverFetchFinished(RedirectFollower *reply) { reply->deleteLater(); - const QString provider = pending_image_loads_.take(reply); + + if (!pending_image_loads_.contains(reply)) return; + CoverSearchResult result = pending_image_loads_.take(reply); statistics_.bytes_transferred_ += reply->bytesAvailable(); @@ -196,18 +208,20 @@ void AlbumCoverFetcherSearch::ProviderCoverFetchFinished(RedirectFollower *reply } if (reply->error() != QNetworkReply::NoError) { - qLog(Info) << "Error requesting" << reply->url() << reply->errorString(); + qLog(Error) << "Error requesting" << reply->url() << reply->errorString(); } else { QImage image; - if (!image.loadFromData(reply->readAll())) { - qLog(Info) << "Error decoding image data from" << reply->url(); + if (image.loadFromData(reply->readAll())) { + + result.score += ScoreImage(image); + candidate_images_.insertMulti(result.score, CandidateImage(result, image)); + + qLog(Debug) << reply->url() << "from" << result.provider << "scored" << result.score; + } else { - const float score = ScoreImage(image); - candidate_images_.insertMulti(score, CandidateImage(provider, image)); - - qLog(Debug) << reply->url() << "scored" << score; + qLog(Error) << "Error decoding image data from" << reply->url(); } } @@ -241,7 +255,7 @@ float AlbumCoverFetcherSearch::ScoreImage(const QImage &image) const { const float size_score = std::sqrt(float(image.width() * image.height())) / kTargetSize; // A 1:1 image scores 1.0, anything else scores less - const float aspect_score = 1.0 - float(image.height() - image.width()) / std::max(image.height(), image.width()); + const float aspect_score = 1.0 - float(std::max(image.width(), image.height()) - std::min(image.width(), image.height())) / std::max(image.height(), image.width()); return size_score + aspect_score; @@ -255,7 +269,9 @@ void AlbumCoverFetcherSearch::SendBestImage() { const CandidateImage best_image = candidate_images_.values().back(); image = best_image.second; - statistics_.chosen_images_by_provider_[best_image.first]++; + qLog(Info) << "Using " << best_image.first.image_url << "from" << best_image.first.provider << "with score" << best_image.first.score; + + statistics_.chosen_images_by_provider_[best_image.first.provider]++; statistics_.chosen_images_++; statistics_.chosen_width_ += image.width(); statistics_.chosen_height_ += image.height(); diff --git a/src/covermanager/albumcoverfetchersearch.h b/src/covermanager/albumcoverfetchersearch.h index c4f81f7c2..e11923fc8 100644 --- a/src/covermanager/albumcoverfetchersearch.h +++ b/src/covermanager/albumcoverfetchersearch.h @@ -92,11 +92,11 @@ class AlbumCoverFetcherSearch : public QObject { CoverSearchResults results_; QMap pending_requests_; - QMap pending_image_loads_; + QMap pending_image_loads_; NetworkTimeouts* image_load_timeout_; - // QMap is sorted by key (score). Values are (provider_name, image) - typedef QPair CandidateImage; + // QMap is sorted by key (score). Values are (result, image) + typedef QPair CandidateImage; QMap candidate_images_; QNetworkAccessManager *network_; diff --git a/src/covermanager/albumcovermanager.cpp b/src/covermanager/albumcovermanager.cpp index 59cf673df..cebd78b1e 100644 --- a/src/covermanager/albumcovermanager.cpp +++ b/src/covermanager/albumcovermanager.cpp @@ -81,6 +81,7 @@ #include "ui_albumcovermanager.h" +using std::unique_ptr; using std::stable_sort; const char *AlbumCoverManager::kSettingsGroup = "CoverManager"; diff --git a/src/covermanager/albumcoversearcher.cpp b/src/covermanager/albumcoversearcher.cpp index d7384d878..3d38d11d7 100644 --- a/src/covermanager/albumcoversearcher.cpp +++ b/src/covermanager/albumcoversearcher.cpp @@ -216,7 +216,7 @@ void AlbumCoverSearcher::SearchFinished(quint64 id, const CoverSearchResults &re QStandardItem *item = new QStandardItem; item->setIcon(no_cover_icon_); - item->setText(result.description); + item->setText(result.artist + " - " + result.album); item->setData(result.image_url, Role_ImageURL); item->setData(id, Role_ImageRequestId); item->setData(false, Role_ImageFetchFinished); diff --git a/src/covermanager/coverprovider.cpp b/src/covermanager/coverprovider.cpp index 56d2cfd30..232e007e3 100644 --- a/src/covermanager/coverprovider.cpp +++ b/src/covermanager/coverprovider.cpp @@ -26,5 +26,5 @@ #include "core/application.h" #include "coverprovider.h" -CoverProvider::CoverProvider(const QString &name, const bool &fetchall, Application *app, QObject *parent) - : QObject(parent), app_(app), name_(name), fetchall_(fetchall) {} +CoverProvider::CoverProvider(const QString &name, const float &quality, const bool &fetchall, Application *app, QObject *parent) + : QObject(parent), app_(app), name_(name), quality_(quality), fetchall_(fetchall) {} diff --git a/src/covermanager/coverprovider.h b/src/covermanager/coverprovider.h index ce3c5a276..f87c56cb1 100644 --- a/src/covermanager/coverprovider.h +++ b/src/covermanager/coverprovider.h @@ -38,10 +38,11 @@ class CoverProvider : public QObject { Q_OBJECT public: - explicit CoverProvider(const QString &name, const bool &fetchall, Application *app, QObject *parent); + explicit CoverProvider(const QString &name, const float &quality, const bool &fetchall, Application *app, QObject *parent); // A name (very short description) of this provider, like "last.fm". QString name() const { return name_; } + bool quality() const { return quality_; } bool fetchall() const { return fetchall_; } // Starts searching for covers matching the given query text. @@ -57,6 +58,7 @@ class CoverProvider : public QObject { private: Application *app_; QString name_; + float quality_; bool fetchall_; }; diff --git a/src/covermanager/deezercoverprovider.cpp b/src/covermanager/deezercoverprovider.cpp index 0cb4ad7ec..13cbb7a7d 100644 --- a/src/covermanager/deezercoverprovider.cpp +++ b/src/covermanager/deezercoverprovider.cpp @@ -48,7 +48,7 @@ const char *DeezerCoverProvider::kApiUrl = "https://api.deezer.com"; const int DeezerCoverProvider::kLimit = 10; -DeezerCoverProvider::DeezerCoverProvider(Application *app, QObject *parent): CoverProvider("Deezer", true, app, parent), network_(new NetworkAccessManager(this)) {} +DeezerCoverProvider::DeezerCoverProvider(Application *app, QObject *parent): CoverProvider("Deezer", 2.0, true, app, parent), network_(new NetworkAccessManager(this)) {} bool DeezerCoverProvider::StartSearch(const QString &artist, const QString &album, int id) { @@ -269,8 +269,12 @@ void DeezerCoverProvider::HandleSearchReply(QNetworkReply *reply, int id) { } QUrl url(cover); + album.remove(Song::kAlbumRemoveDisc); + album.remove(Song::kAlbumRemoveMisc); + CoverSearchResult cover_result; - cover_result.description = artist + " " + album; + cover_result.artist = artist; + cover_result.album = album; cover_result.image_url = url; results << cover_result; diff --git a/src/covermanager/discogscoverprovider.cpp b/src/covermanager/discogscoverprovider.cpp index 25a49f69d..5345d9dcc 100644 --- a/src/covermanager/discogscoverprovider.cpp +++ b/src/covermanager/discogscoverprovider.cpp @@ -54,7 +54,7 @@ const char *DiscogsCoverProvider::kUrlReleases = "https://api.discogs.com/releas const char *DiscogsCoverProvider::kAccessKeyB64 = "dGh6ZnljUGJlZ1NEeXBuSFFxSVk="; const char *DiscogsCoverProvider::kSecretKeyB64 = "ZkFIcmlaSER4aHhRSlF2U3d0bm5ZVmdxeXFLWUl0UXI="; -DiscogsCoverProvider::DiscogsCoverProvider(Application *app, QObject *parent) : CoverProvider("Discogs", false, app, parent), network_(new NetworkAccessManager(this)) {} +DiscogsCoverProvider::DiscogsCoverProvider(Application *app, QObject *parent) : CoverProvider("Discogs", 0.0, false, app, parent), network_(new NetworkAccessManager(this)) {} bool DiscogsCoverProvider::StartSearch(const QString &artist, const QString &album, int s_id) { @@ -76,7 +76,7 @@ void DiscogsCoverProvider::CancelSearch(int id) { } -bool DiscogsCoverProvider::StartRelease(DiscogsCoverSearchContext *s_ctx, int r_id, QString resource_url) { +bool DiscogsCoverProvider::StartRelease(DiscogsCoverSearchContext *s_ctx, int r_id, QString &description, QString &resource_url) { DiscogsCoverReleaseContext *r_ctx = new DiscogsCoverReleaseContext; @@ -225,7 +225,7 @@ QJsonObject DiscogsCoverProvider::ExtractJsonObj(const QByteArray &data) { Error("Reply from server missing Json data.", data); return QJsonObject(); } - if (json_doc.isNull() || json_doc.isEmpty()) { + if (json_doc.isEmpty()) { Error("Received empty Json document.", json_doc); return QJsonObject(); } @@ -307,7 +307,7 @@ void DiscogsCoverProvider::HandleSearchReply(QNetworkReply *reply, int s_id) { QString title = json_obj["title"].toString(); QString resource_url = json_obj["resource_url"].toString(); if (resource_url.isEmpty()) continue; - StartRelease(s_ctx, r_id, resource_url); + StartRelease(s_ctx, r_id, title, resource_url); i++; } @@ -337,41 +337,96 @@ void DiscogsCoverProvider::HandleReleaseReply(QNetworkReply *reply, int s_id, in QByteArray data = GetReplyData(reply); if (data.isEmpty()) { - EndSearch(s_ctx); - return; - } - - QJsonValue json_value = ExtractData(data, "images", true); - if (!json_value.isArray()) { EndSearch(s_ctx, r_ctx); return; } - QJsonArray json_images = json_value.toArray(); - if (json_images.isEmpty()) { + QJsonObject json_obj = ExtractJsonObj(data); + if (json_obj.isEmpty()) { + EndSearch(s_ctx, r_ctx); + return; + } + + if (!json_obj.contains("artists") || !json_obj.contains("title") || !json_obj.contains("images")) { + Error("Json reply is missing artists, title or images."); + EndSearch(s_ctx, r_ctx); + return; + } + + QJsonValue json_artists = json_obj["artists"]; + if (!json_artists.isArray()) { + Error("Json artists is not a array.", json_artists); + EndSearch(s_ctx, r_ctx); + return; + } + QJsonArray json_array_artists = json_artists.toArray(); + int i = 0; + QString artist; + for (const QJsonValue &value : json_array_artists) { + if (!value.isObject()) { + Error("Invalid Json reply, value is not a object.", value); + continue; + } + QJsonObject json_obj_artist = value.toObject(); + if (!json_obj_artist.contains("name") ) { + Error("Invalid Json reply, artists is missing name.", json_obj_artist); + continue; + } + artist = json_obj_artist["name"].toString(); + ++i; + if (artist == s_ctx->artist) break; + } + if (artist.isEmpty()) { + EndSearch(s_ctx, r_ctx); + return; + } + if (i > 1 && artist != s_ctx->artist) artist = "Various artists"; + + QString album = json_obj["title"].toString(); + if (artist != s_ctx->artist && album != s_ctx->album) { + EndSearch(s_ctx, r_ctx); + return; + } + + QJsonValue json_images = json_obj["images"]; + if (!json_images.isArray()) { + Error("Json images is not an array."); + EndSearch(s_ctx, r_ctx); + return; + } + QJsonArray json_array_images = json_images.toArray(); + + if (json_array_images.isEmpty()) { Error("Json array is empty."); EndSearch(s_ctx, r_ctx); return; } - int i = 0; - for (const QJsonValue &value : json_images) { + i = 0; + for (const QJsonValue &value : json_array_images) { + if (!value.isObject()) { Error("Invalid Json reply, value is not an object.", value); continue; } QJsonObject json_obj = value.toObject(); - if (!json_obj.contains("type") || !json_obj.contains("resource_url")) { - Error("Invalid Json reply, value is missing ID or resource_url.", json_obj); + if (!json_obj.contains("type") || !json_obj.contains("resource_url") || !json_obj.contains("width") || !json_obj.contains("height") ) { + Error("Invalid Json reply, images value is missing type, resource_url, width or height.", json_obj); continue; } - CoverSearchResult cover_result; - cover_result.description = s_ctx->title; QString type = json_obj["type"].toString(); i++; if (type != "primary") { continue; } + int width = json_obj["width"].toInt(); + int height = json_obj["height"].toInt(); + if (width < 300 || height < 300) continue; + const float aspect_score = 1.0 - float(std::max(width, height) - std::min(width, height)) / std::max(height, width); + if (aspect_score < 0.85) continue; + CoverSearchResult cover_result; + cover_result.artist = artist; + cover_result.album = album; cover_result.image_url = QUrl(json_obj["resource_url"].toString()); if (cover_result.image_url.isEmpty()) continue; s_ctx->results.append(cover_result); @@ -400,7 +455,7 @@ void DiscogsCoverProvider::EndSearch(DiscogsCoverSearchContext *s_ctx) { } -void DiscogsCoverProvider::EndSearch(DiscogsCoverReleaseContext* r_ctx) { +void DiscogsCoverProvider::EndSearch(DiscogsCoverReleaseContext *r_ctx) { delete requests_release_.take(r_ctx->id); } diff --git a/src/covermanager/discogscoverprovider.h b/src/covermanager/discogscoverprovider.h index 270f49f5f..8e811833a 100644 --- a/src/covermanager/discogscoverprovider.h +++ b/src/covermanager/discogscoverprovider.h @@ -47,7 +47,6 @@ struct DiscogsCoverSearchContext { // The search query QString artist; QString album; - QString title; int r_count; CoverSearchResults results; @@ -88,7 +87,7 @@ class DiscogsCoverProvider : public CoverProvider { QHash requests_search_; QHash requests_release_; - bool StartRelease(DiscogsCoverSearchContext *s_ctx, int r_id, QString resource_url); + bool StartRelease(DiscogsCoverSearchContext *s_ctx, int r_id, QString &description, QString &resource_url); void SendSearchRequest(DiscogsCoverSearchContext *s_ctx); void SendReleaseRequest(DiscogsCoverSearchContext *s_ctx, DiscogsCoverReleaseContext *r_ctx); diff --git a/src/covermanager/lastfmcoverprovider.cpp b/src/covermanager/lastfmcoverprovider.cpp index 5af34f8d2..0d1b65812 100644 --- a/src/covermanager/lastfmcoverprovider.cpp +++ b/src/covermanager/lastfmcoverprovider.cpp @@ -48,7 +48,7 @@ const char *LastFmCoverProvider::kUrl = "https://ws.audioscrobbler.com/2.0/"; const char *LastFmCoverProvider::kApiKey = "211990b4c96782c05d1536e7219eb56e"; const char *LastFmCoverProvider::kSecret = "80fd738f49596e9709b1bf9319c444a8"; -LastFmCoverProvider::LastFmCoverProvider(Application *app, QObject *parent) : CoverProvider("last.fm", true, app, parent), network_(new NetworkAccessManager(this)) {} +LastFmCoverProvider::LastFmCoverProvider(Application *app, QObject *parent) : CoverProvider("last.fm", 1.0, true, app, parent), network_(new NetworkAccessManager(this)) {} bool LastFmCoverProvider::StartSearch(const QString &artist, const QString &album, int id) { @@ -156,8 +156,8 @@ void LastFmCoverProvider::QueryFinished(QNetworkReply *reply, int id) { continue; } QString artist = json_obj["artist"].toString(); - QString name = json_obj["name"].toString(); - + QString album = json_obj["name"].toString(); + QJsonValue json_image = json_obj["image"]; if (!json_image.isArray()) { Error("Invalid Json reply, album image is not an array.", json_image); @@ -187,7 +187,8 @@ void LastFmCoverProvider::QueryFinished(QNetworkReply *reply, int id) { if (url.isEmpty()) continue; CoverSearchResult cover_result; - cover_result.description = artist + " " + name; + cover_result.artist = artist; + cover_result.album = album; cover_result.image_url = url; results << cover_result; } diff --git a/src/covermanager/musicbrainzcoverprovider.cpp b/src/covermanager/musicbrainzcoverprovider.cpp index d3476818f..43d5f4855 100644 --- a/src/covermanager/musicbrainzcoverprovider.cpp +++ b/src/covermanager/musicbrainzcoverprovider.cpp @@ -49,7 +49,7 @@ const char *MusicbrainzCoverProvider::kReleaseSearchUrl = "https://musicbrainz.o const char *MusicbrainzCoverProvider::kAlbumCoverUrl = "https://coverartarchive.org/release/%1/front"; const int MusicbrainzCoverProvider::kLimit = 8; -MusicbrainzCoverProvider::MusicbrainzCoverProvider(Application *app, QObject *parent): CoverProvider("MusicBrainz", true, app, parent), network_(new NetworkAccessManager(this)) {} +MusicbrainzCoverProvider::MusicbrainzCoverProvider(Application *app, QObject *parent): CoverProvider("MusicBrainz", 1.5, true, app, parent), network_(new NetworkAccessManager(this)) {} bool MusicbrainzCoverProvider::StartSearch(const QString &artist, const QString &album, int id) { @@ -117,22 +117,60 @@ void MusicbrainzCoverProvider::HandleSearchReply(QNetworkReply *reply, int searc } for (const QJsonValue &value : json_releases) { + if (!value.isObject()) { Error("Invalid Json reply, album value is not an object.", value); continue; } QJsonObject json_obj = value.toObject(); - if (!json_obj.contains("id") || !json_obj.contains("title")) { - Error("Invalid Json reply, album is missing id or title.", json_obj); + if (!json_obj.contains("id") || !json_obj.contains("artist-credit") || !json_obj.contains("title")) { + Error("Invalid Json reply, album is missing id, artist-credit or title.", json_obj); continue; } + + QJsonValue json_artists = json_obj["artist-credit"]; + if (!json_artists.isArray()) { + Error("Json artist-credit is not an array.", json_artists); + continue; + } + QJsonArray json_array_artists = json_artists.toArray(); + int i = 0; + QString artist; + for (const QJsonValue &json_value_artist : json_array_artists) { + if (!json_value_artist.isObject()) { + Error("Invalid Json reply, artist is not an object.", json_value_artist); + continue; + } + QJsonObject json_obj_artist = json_value_artist.toObject(); + + if (!json_obj_artist.contains("artist") ) { + Error("Invalid Json reply, artist is missing.", json_obj_artist); + continue; + } + QJsonValue json_value_artist2 = json_obj_artist["artist"]; + if (!json_value_artist2.isObject()) { + Error("Invalid Json reply, artist is not an object.", json_value_artist2); + continue; + } + QJsonObject json_obj_artist2 = json_value_artist2.toObject(); + + if (!json_obj_artist2.contains("name") ) { + Error("Invalid Json reply, artist is missing name.", json_value_artist2); + continue; + } + artist = json_obj_artist2["name"].toString(); + ++i; + } + if (i > 1) artist = "Various artists"; + QString id = json_obj["id"].toString(); - QString title = json_obj["title"].toString(); - CoverSearchResult result; + QString album = json_obj["title"].toString(); + CoverSearchResult cover_result; QUrl url(QString(kAlbumCoverUrl).arg(id)); - result.description = title; - result.image_url = url; - results.append(result); + cover_result.artist = artist; + cover_result.album = album; + cover_result.image_url = url; + results.append(cover_result); } emit SearchFinished(search_id, results); diff --git a/src/covermanager/tidalcoverprovider.cpp b/src/covermanager/tidalcoverprovider.cpp index 934b6ff68..1740f2bed 100644 --- a/src/covermanager/tidalcoverprovider.cpp +++ b/src/covermanager/tidalcoverprovider.cpp @@ -54,7 +54,7 @@ const char *TidalCoverProvider::kApiTokenB64 = "UDVYYmVvNUxGdkVTZUR5Ng=="; const int TidalCoverProvider::kLimit = 10; TidalCoverProvider::TidalCoverProvider(Application *app, QObject *parent) : - CoverProvider("Tidal", true, app, parent), + CoverProvider("Tidal", 2.0, true, app, parent), service_(app->internet_services()->Service()), network_(new NetworkAccessManager(this)) { @@ -161,7 +161,7 @@ QJsonObject TidalCoverProvider::ExtractJsonObj(QByteArray &data, QString &error) return QJsonObject(); } - if (json_doc.isNull() || json_doc.isEmpty()) { + if (json_doc.isEmpty()) { error = Error("Received empty Json document.", data); return QJsonObject(); } @@ -256,11 +256,15 @@ void TidalCoverProvider::HandleSearchReply(QNetworkReply *reply, int id) { } QString artist = json_artist["name"].toString(); + album.remove(Song::kAlbumRemoveDisc); + album.remove(Song::kAlbumRemoveMisc); + cover = cover.replace("-", "/"); QUrl cover_url (QString("%1/images/%2/%3.jpg").arg(kResourcesUrl).arg(cover).arg("1280x1280")); CoverSearchResult cover_result; - cover_result.description = artist + " " + album; + cover_result.artist = artist; + cover_result.album = album; cover_result.image_url = cover_url; results << cover_result;