Improve cover providers score system
This commit is contained in:
parent
ca928bdacb
commit
63d5018ad6
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<CoverSearchResult> &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 QList<CoverSe
|
|||
}
|
||||
|
||||
AllProvidersFinished();
|
||||
|
||||
}
|
||||
|
||||
void AlbumCoverFetcherSearch::AllProvidersFinished() {
|
||||
|
@ -171,7 +181,7 @@ void AlbumCoverFetcherSearch::FetchMoreImages() {
|
|||
|
||||
RedirectFollower *image_reply = new RedirectFollower(network_->get(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();
|
||||
|
|
|
@ -92,11 +92,11 @@ class AlbumCoverFetcherSearch : public QObject {
|
|||
CoverSearchResults results_;
|
||||
|
||||
QMap<int, CoverProvider*> pending_requests_;
|
||||
QMap<RedirectFollower*, QString> pending_image_loads_;
|
||||
QMap<RedirectFollower*, CoverSearchResult> pending_image_loads_;
|
||||
NetworkTimeouts* image_load_timeout_;
|
||||
|
||||
// QMap is sorted by key (score). Values are (provider_name, image)
|
||||
typedef QPair<QString, QImage> CandidateImage;
|
||||
// QMap is sorted by key (score). Values are (result, image)
|
||||
typedef QPair<CoverSearchResult, QImage> CandidateImage;
|
||||
QMap<float, CandidateImage> candidate_images_;
|
||||
|
||||
QNetworkAccessManager *network_;
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
|
||||
#include "ui_albumcovermanager.h"
|
||||
|
||||
using std::unique_ptr;
|
||||
using std::stable_sort;
|
||||
|
||||
const char *AlbumCoverManager::kSettingsGroup = "CoverManager";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {}
|
||||
|
|
|
@ -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_;
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<int, DiscogsCoverSearchContext*> requests_search_;
|
||||
QHash<int, DiscogsCoverReleaseContext*> 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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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<TidalService>()),
|
||||
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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue