From 264c6e259b21c7848d33a1a518624c181842bbcb Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sat, 29 Jun 2019 19:57:20 +0200 Subject: [PATCH] Respect rate limiting when fetching tags from musicbrainz --- src/core/mainwindow.cpp | 2 +- src/dialogs/edittagdialog.cpp | 2 +- src/musicbrainz/acoustidclient.cpp | 34 ++-- src/musicbrainz/acoustidclient.h | 12 +- src/musicbrainz/musicbrainzclient.cpp | 248 +++++++++++++++++--------- src/musicbrainz/musicbrainzclient.h | 47 +++-- src/musicbrainz/tagfetcher.cpp | 16 +- src/musicbrainz/tagfetcher.h | 10 +- 8 files changed, 239 insertions(+), 132 deletions(-) diff --git a/src/core/mainwindow.cpp b/src/core/mainwindow.cpp index bc840a12a..50087fa9f 100644 --- a/src/core/mainwindow.cpp +++ b/src/core/mainwindow.cpp @@ -2338,7 +2338,7 @@ void MainWindow::AutoCompleteTags() { track_selection_dialog_->set_save_on_close(true); connect(tag_fetcher_.get(), SIGNAL(ResultAvailable(Song, SongList)), track_selection_dialog_.get(), SLOT(FetchTagFinished(Song, SongList)), Qt::QueuedConnection); - connect(tag_fetcher_.get(), SIGNAL(Progress(Song, QString)), track_selection_dialog_.get(), SLOT(FetchTagProgress(Song,QString))); + connect(tag_fetcher_.get(), SIGNAL(Progress(Song, QString)), track_selection_dialog_.get(), SLOT(FetchTagProgress(Song, QString))); connect(track_selection_dialog_.get(), SIGNAL(accepted()), SLOT(AutoCompleteTagsAccepted())); connect(track_selection_dialog_.get(), SIGNAL(finished(int)), tag_fetcher_.get(), SLOT(Cancel())); connect(track_selection_dialog_.get(), SIGNAL(Error(QString)), SLOT(ShowErrorDialog(QString))); diff --git a/src/dialogs/edittagdialog.cpp b/src/dialogs/edittagdialog.cpp index 6efa0013f..c6db1667d 100644 --- a/src/dialogs/edittagdialog.cpp +++ b/src/dialogs/edittagdialog.cpp @@ -111,7 +111,7 @@ EditTagDialog::EditTagDialog(Application *app, QWidget *parent) #if defined(HAVE_GSTREAMER) && defined(HAVE_CHROMAPRINT) connect(tag_fetcher_, SIGNAL(ResultAvailable(Song, SongList)), results_dialog_, SLOT(FetchTagFinished(Song, SongList)), Qt::QueuedConnection); - connect(tag_fetcher_, SIGNAL(Progress(Song,QString)), results_dialog_, SLOT(FetchTagProgress(Song,QString))); + connect(tag_fetcher_, SIGNAL(Progress(Song, QString)), results_dialog_, SLOT(FetchTagProgress(Song, QString))); connect(results_dialog_, SIGNAL(SongChosen(Song, Song)), SLOT(FetchTagSongChosen(Song, Song))); connect(results_dialog_, SIGNAL(finished(int)), tag_fetcher_, SLOT(Cancel())); #endif diff --git a/src/musicbrainz/acoustidclient.cpp b/src/musicbrainz/acoustidclient.cpp index 98d7aa500..3bd65a0ff 100644 --- a/src/musicbrainz/acoustidclient.cpp +++ b/src/musicbrainz/acoustidclient.cpp @@ -58,25 +58,25 @@ AcoustidClient::AcoustidClient(QObject *parent) network_(new NetworkAccessManager(this)), timeouts_(new NetworkTimeouts(kDefaultTimeout, this)) {} -void AcoustidClient::SetTimeout(int msec) { timeouts_->SetTimeout(msec); } +void AcoustidClient::SetTimeout(const int msec) { timeouts_->SetTimeout(msec); } -void AcoustidClient::Start(int id, const QString &fingerprint, int duration_msec) { +void AcoustidClient::Start(const int id, const QString &fingerprint, int duration_msec) { typedef QPair Param; + typedef QList ParamList; - QList parameters; - parameters << Param("format", "json") - << Param("client", kClientId) - << Param("duration", QString::number(duration_msec / kMsecPerSec)) - << Param("meta", "recordingids+sources") - << Param("fingerprint", fingerprint); + const ParamList params = ParamList () << Param("format", "json") + << Param("client", kClientId) + << Param("duration", QString::number(duration_msec / kMsecPerSec)) + << Param("meta", "recordingids+sources") + << Param("fingerprint", fingerprint); - QUrl url(kUrl); QUrlQuery url_query; - url_query.setQueryItems(parameters); + url_query.setQueryItems(params); + QUrl url(kUrl); url.setQuery(url_query); - QNetworkRequest req(url); + QNetworkRequest req(url); QNetworkReply *reply = network_->get(req); NewClosure(reply, SIGNAL(finished()), this, SLOT(RequestFinished(QNetworkReply*, int)), reply, id); requests_[id] = reply; @@ -84,11 +84,15 @@ void AcoustidClient::Start(int id, const QString &fingerprint, int duration_msec timeouts_->AddReply(reply); } -void AcoustidClient::Cancel(int id) { delete requests_.take(id); } +void AcoustidClient::Cancel(const int id) { + if (requests_.contains(id)) delete requests_.take(id); +} void AcoustidClient::CancelAll() { + qDeleteAll(requests_.values()); requests_.clear(); + } namespace { @@ -107,12 +111,12 @@ struct IdSource { }; } -void AcoustidClient::RequestFinished(QNetworkReply *reply, int request_id) { +void AcoustidClient::RequestFinished(QNetworkReply *reply, const int request_id) { reply->deleteLater(); requests_.remove(request_id); - if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + if (reply->error() != QNetworkReply::NoError || reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { emit Finished(request_id, QStringList()); return; } @@ -129,7 +133,7 @@ void AcoustidClient::RequestFinished(QNetworkReply *reply, int request_id) { QString status = json_object["status"].toString(); if (status != "ok") { - emit Finished(request_id, QStringList()); + emit Finished(request_id, QStringList(), status); return; } diff --git a/src/musicbrainz/acoustidclient.h b/src/musicbrainz/acoustidclient.h index 9335bf51f..796065077 100644 --- a/src/musicbrainz/acoustidclient.h +++ b/src/musicbrainz/acoustidclient.h @@ -46,22 +46,22 @@ class AcoustidClient : public QObject { AcoustidClient(QObject *parent = nullptr); // Network requests will be aborted after this interval. - void SetTimeout(int msec); + void SetTimeout(const int msec); // Starts a request and returns immediately. Finished() will be emitted later with the same ID. - void Start(int id, const QString &fingerprint, int duration_msec); + void Start(const int id, const QString &fingerprint, int duration_msec); // Cancels the request with the given ID. Finished() will never be emitted for that ID. Does nothing if there is no request with the given ID. - void Cancel(int id); + void Cancel(const int id); // Cancels all requests. Finished() will never be emitted for any pending requests. void CancelAll(); -signals: - void Finished(int id, const QStringList &mbid_list); + signals: + void Finished(const int id, const QStringList &mbid_list, const QString &error = QString()); private slots: - void RequestFinished(QNetworkReply *reply, int id); + void RequestFinished(QNetworkReply *reply, const int id); private: static const char *kClientId; diff --git a/src/musicbrainz/musicbrainzclient.cpp b/src/musicbrainz/musicbrainzclient.cpp index 86b4fdf13..4a493f6b1 100644 --- a/src/musicbrainz/musicbrainzclient.cpp +++ b/src/musicbrainz/musicbrainzclient.cpp @@ -2,6 +2,7 @@ * Strawberry Music Player * This file was part of Clementine. * Copyright 2010, David Sansome + * Copyright 2019, Jonas Kvinge * * Strawberry is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,12 +25,10 @@ #include #include -#include #include #include #include #include -#include #include #include #include @@ -37,7 +36,11 @@ #include #include #include +#include +#include +#include #include +#include #include #include "core/closure.h" @@ -52,65 +55,179 @@ using std::stable_sort; const char *MusicBrainzClient::kTrackUrl = "http://musicbrainz.org/ws/2/recording/"; const char *MusicBrainzClient::kDiscUrl = "http://musicbrainz.org/ws/2/discid/"; const char *MusicBrainzClient::kDateRegex = "^[12]\\d{3}"; -const int MusicBrainzClient::kDefaultTimeout = 5000; // msec +const int MusicBrainzClient::kRequestsDelay = 1200; +const int MusicBrainzClient::kDefaultTimeout = 8000; const int MusicBrainzClient::kMaxRequestPerTrack = 3; MusicBrainzClient::MusicBrainzClient(QObject *parent, QNetworkAccessManager *network) : QObject(parent), network_(network ? network : new NetworkAccessManager(this)), - timeouts_(new NetworkTimeouts(kDefaultTimeout, this)) {} + timeouts_(new NetworkTimeouts(kDefaultTimeout, this)), + timer_flush_requests_(new QTimer(this)) { -void MusicBrainzClient::Start(int id, const QStringList &mbid_list) { + timer_flush_requests_->setInterval(kRequestsDelay); + timer_flush_requests_->setSingleShot(true); + connect(timer_flush_requests_, SIGNAL(timeout()), this, SLOT(FlushRequests())); - typedef QPair Param; +} + +QByteArray MusicBrainzClient::GetReplyData(QNetworkReply *reply, QString &error) { + + QByteArray data; + + if (reply->error() == QNetworkReply::NoError && reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) { + data = reply->readAll(); + } + else { + if (reply->error() < 200) { + // This is a network error, there is nothing more to do. + error = Error(QString("%1 (%2)").arg(reply->errorString()).arg(reply->error())); + } + else { + // See if there is Json data containing "error" - then use that instead. + data = reply->readAll(); + QJsonParseError json_error; + QJsonDocument json_doc = QJsonDocument::fromJson(data, &json_error); + if (json_error.error == QJsonParseError::NoError && json_doc.isObject()) { + QJsonObject json_obj = json_doc.object(); + if (!json_obj.isEmpty() && json_obj.contains("error")) { + error = json_obj["error"].toString(); + } + } + if (error.isEmpty()) { + if (reply->error() != QNetworkReply::NoError) { + error = QString("%1 (%2)").arg(reply->errorString()).arg(reply->error()); + } + else { + error = Error(QString("Received HTTP code %1").arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt())); + } + } + error = Error(error); + abort(); + } + return QByteArray(); + } + + return data; + +} + +void MusicBrainzClient::Cancel(int id) { + + while (!requests_.isEmpty() && requests_.contains(id)) { + QNetworkReply *reply = requests_.take(id); + disconnect(reply, 0, nullptr, 0); + if (reply->isRunning()) reply->abort(); + reply->deleteLater(); + } + +} + +void MusicBrainzClient::CancelAll() { + + qDeleteAll(requests_.values()); + requests_.clear(); + +} + +void MusicBrainzClient::Start(const int id, const QStringList &mbid_list) { int request_number = 0; for (const QString &mbid : mbid_list) { - QList parameters; - parameters << Param("inc", "artists+releases+media"); - - QUrl url(kTrackUrl + mbid); - QUrlQuery url_query; - url_query.setQueryItems(parameters); - url.setQuery(url_query); - QNetworkRequest req(url); - - QNetworkReply *reply = network_->get(req); - NewClosure(reply, SIGNAL(finished()), this, SLOT(RequestFinished(QNetworkReply*, int, int)), reply, id, request_number++); - requests_.insert(id, reply); - - timeouts_->AddReply(reply); - - if (request_number >= kMaxRequestPerTrack) { - break; - } + ++request_number; + if (request_number > kMaxRequestPerTrack) break; + Request request(id, mbid, request_number); + requests_pending_.enqueue(request); } + + if (!timer_flush_requests_->isActive()) { + timer_flush_requests_->start(); + } + } void MusicBrainzClient::StartDiscIdRequest(const QString &discid) { - typedef QPair Param; + const ParamList params = ParamList() << Param("inc", "artists+recordings"); - QList parameters; - parameters << Param("inc", "artists+recordings"); - - QUrl url(kDiscUrl + discid); QUrlQuery url_query; - url_query.setQueryItems(parameters); + url_query.setQueryItems(params); + QUrl url(kDiscUrl + discid); url.setQuery(url_query); - QNetworkRequest req(url); + QNetworkRequest req(url); QNetworkReply *reply = network_->get(req); NewClosure(reply, SIGNAL(finished()), this, SLOT(DiscIdRequestFinished(const QString&, QNetworkReply*)), discid, reply); timeouts_->AddReply(reply); + } -void MusicBrainzClient::Cancel(int id) { delete requests_.take(id); } +void MusicBrainzClient::FlushRequests() { + + if (!requests_.isEmpty() || requests_pending_.isEmpty()) return; + + Request request = requests_pending_.dequeue(); + + const ParamList params = ParamList() << Param("inc", "artists+releases+media"); + + QUrlQuery url_query; + url_query.setQueryItems(params); + QUrl url(kTrackUrl + request.mbid); + url.setQuery(url_query); + + QNetworkRequest req(url); + QNetworkReply *reply = network_->get(req); + NewClosure(reply, SIGNAL(finished()), this, SLOT(RequestFinished(QNetworkReply*, const int, const int)), reply, request.id, request.number); + requests_.insert(request.id, reply); + + timeouts_->AddReply(reply); + +} + +void MusicBrainzClient::RequestFinished(QNetworkReply *reply, const int id, const int request_number) { + + reply->deleteLater(); + + const int nb_removed = requests_.remove(id, reply); + if (nb_removed != 1) { + qLog(Debug) << "MusicBrainz: Unknown reply received:" << nb_removed << "requests removed, while only one was supposed to be removed"; + } + + if (!timer_flush_requests_->isActive() && requests_.isEmpty() && !requests_pending_.isEmpty()) { + timer_flush_requests_->start(); + } + + QString error; + QByteArray data = GetReplyData(reply, error); + if (!data.isEmpty()) { + QXmlStreamReader reader(data); + ResultList res; + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement && reader.name() == "recording") { + ResultList tracks = ParseTrack(&reader); + for (const Result &track : tracks) { + if (!track.title_.isEmpty()) { + res << track; + } + } + } + } + pending_results_[id] << PendingResults(request_number, res); + } + + // No more pending requests for this id: emit the results we have. + if (!requests_.contains(id)) { + // Merge the results we have + ResultList ret; + QList result_list_list = pending_results_.take(id); + std::sort(result_list_list.begin(), result_list_list.end()); + for (const PendingResults &result_list : result_list_list) { + ret << result_list.results_; + } + emit Finished(id, UniqueResults(ret, KeepOriginalOrder), error); + } -void MusicBrainzClient::CancelAll() { - qDeleteAll(requests_.values()); - requests_.clear(); } void MusicBrainzClient::DiscIdRequestFinished(const QString &discid, QNetworkReply *reply) { @@ -122,10 +239,10 @@ void MusicBrainzClient::DiscIdRequestFinished(const QString &discid, QNetworkRep QString album; int year = 0; - if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { - qLog(Error) << "Error:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() << "http status code received"; - qLog(Error) << reply->readAll(); - emit Finished(artist, album, ret); + QString error; + QByteArray data = GetReplyData(reply, error); + if (data.isEmpty()) { + emit Finished(artist, album, ret, error); return; } @@ -136,7 +253,7 @@ void MusicBrainzClient::DiscIdRequestFinished(const QString &discid, QNetworkRep // -get all the tracks' tags // Note: If there are multiple releases for the discid, the first // release is chosen. - QXmlStreamReader reader(reply); + QXmlStreamReader reader(data); while (!reader.atEnd()) { QXmlStreamReader::TokenType type = reader.readNext(); if (type == QXmlStreamReader::StartElement) { @@ -188,49 +305,7 @@ void MusicBrainzClient::DiscIdRequestFinished(const QString &discid, QNetworkRep } emit Finished(artist, album, UniqueResults(ret, SortResults)); -} -void MusicBrainzClient::RequestFinished(QNetworkReply *reply, int id, int request_number) { - - reply->deleteLater(); - - const int nb_removed = requests_.remove(id, reply); - if (nb_removed != 1) { - qLog(Error) << "Error: unknown reply received:" << nb_removed << - "requests removed, while only one was supposed to be removed"; - } - - if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) { - QXmlStreamReader reader(reply); - ResultList res; - while (!reader.atEnd()) { - if (reader.readNext() == QXmlStreamReader::StartElement && reader.name() == "recording") { - ResultList tracks = ParseTrack(&reader); - for (const Result &track : tracks) { - if (!track.title_.isEmpty()) { - res << track; - } - } - } - } - pending_results_[id] << PendingResults(request_number, res); - } - else { - qLog(Error) << "Error:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() << "http status code received"; - qLog(Error) << reply->readAll(); - } - - // No more pending requests for this id: emit the results we have. - if (!requests_.contains(id)) { - // Merge the results we have - ResultList ret; - QList result_list_list = pending_results_.take(id); - std::sort(result_list_list.begin(), result_list_list.end()); - for (const PendingResults &result_list : result_list_list) { - ret << result_list.results_; - } - emit Finished(id, UniqueResults(ret, KeepOriginalOrder)); - } } bool MusicBrainzClient::MediumHasDiscid(const QString &discid, QXmlStreamReader *reader) { @@ -420,3 +495,12 @@ MusicBrainzClient::ResultList MusicBrainzClient::UniqueResults(const ResultList& } return ret; } + +QString MusicBrainzClient::Error(QString error, QVariant debug) { + + qLog(Error) << "MusicBrainz:" << error; + if (debug.isValid()) qLog(Debug) << debug; + + return error; + +} diff --git a/src/musicbrainz/musicbrainzclient.h b/src/musicbrainz/musicbrainzclient.h index 4f5d41745..359036f1e 100644 --- a/src/musicbrainz/musicbrainzclient.h +++ b/src/musicbrainz/musicbrainzclient.h @@ -2,6 +2,7 @@ * Strawberry Music Player * This file was part of Clementine. * Copyright 2010, David Sansome + * Copyright 2019, Jonas Kvinge * * Strawberry is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,17 +29,16 @@ #include #include #include -#include #include #include -#include +#include #include #include -#include #include #include -#include +class QNetworkAccessManager; +class QTimer; class NetworkTimeouts; class MusicBrainzClient : public QObject { @@ -91,7 +91,7 @@ class MusicBrainzClient : public QObject { typedef QList ResultList; // Starts a request and returns immediately. Finished() will be emitted later with the same ID. - void Start(int id, const QStringList &mbid); + void Start(const int id, const QStringList &mbid); void StartDiscIdRequest(const QString &discid); // Cancels the request with the given ID. Finished() will never be emitted for that ID. Does nothing if there is no request with the given ID. @@ -100,18 +100,30 @@ class MusicBrainzClient : public QObject { // Cancels all requests. Finished() will never be emitted for any pending requests. void CancelAll(); -signals: + signals: // Finished signal emitted when fechting songs tags - void Finished(int id, const MusicBrainzClient::ResultList& result); + void Finished(const int id, const MusicBrainzClient::ResultList &result, const QString &error = QString()); // Finished signal emitted when fechting album's songs tags using DiscId - void Finished(const QString& artist, const QString album, const MusicBrainzClient::ResultList& result); + void Finished(const QString &artist, const QString &album, const MusicBrainzClient::ResultList &result, const QString &error = QString()); private slots: + void FlushRequests(); // id identifies the track, and request_number means it's the 'request_number'th request for this track - void RequestFinished(QNetworkReply* reply, int id, int request_number); - void DiscIdRequestFinished(const QString& discid, QNetworkReply* reply); + void RequestFinished(QNetworkReply* reply, const int id, const int request_number); + void DiscIdRequestFinished(const QString &discid, QNetworkReply* reply); private: + typedef QPair Param; + typedef QList ParamList; + + struct Request { + Request() : id(0), number(0) {} + Request(const int _id, const QString &_mbid, const int _number) : id(_id), mbid(_mbid), number(_number) {} + int id; + QString mbid; + int number; + }; + // Used as parameter for UniqueResults enum UniqueResultsSortOption { SortResults = 0, @@ -178,6 +190,8 @@ signals: ResultList results_; }; + QNetworkReply *CreateRequest(const QString &ressource, const ParamList ¶ms_provided); + QByteArray GetReplyData(QNetworkReply *reply, QString &error); static bool MediumHasDiscid(const QString& discid, QXmlStreamReader* reader); static ResultList ParseMedium(QXmlStreamReader* reader); static Result ParseTrackFromDisc(QXmlStreamReader* reader); @@ -185,20 +199,25 @@ signals: static void ParseArtist(QXmlStreamReader* reader, QString* artist); static Release ParseRelease(QXmlStreamReader* reader); static ResultList UniqueResults(const ResultList& results, UniqueResultsSortOption opt = SortResults); - + QString Error(QString error, QVariant debug = QVariant()); private: - static const char* kTrackUrl; - static const char* kDiscUrl; - static const char* kDateRegex; + + static const char *kTrackUrl; + static const char *kDiscUrl; + static const char *kDateRegex; + static const int kRequestsDelay; static const int kDefaultTimeout; static const int kMaxRequestPerTrack; QNetworkAccessManager* network_; NetworkTimeouts* timeouts_; + QQueue requests_pending_; QMultiMap requests_; // Results we received so far, kept here until all the replies are finished QMap> pending_results_; + QTimer *timer_flush_requests_; + }; inline uint qHash(const MusicBrainzClient::Result& result) { diff --git a/src/musicbrainz/tagfetcher.cpp b/src/musicbrainz/tagfetcher.cpp index 16f5c1be0..0c49b65d0 100644 --- a/src/musicbrainz/tagfetcher.cpp +++ b/src/musicbrainz/tagfetcher.cpp @@ -39,8 +39,8 @@ TagFetcher::TagFetcher(QObject *parent) acoustid_client_(new AcoustidClient(this)), musicbrainz_client_(new MusicBrainzClient(this)) { - connect(acoustid_client_, SIGNAL(Finished(int, QStringList)), SLOT(PuidsFound(int, QStringList))); - connect(musicbrainz_client_, SIGNAL(Finished(int, MusicBrainzClient::ResultList)), SLOT(TagsFetched(int, MusicBrainzClient::ResultList))); + connect(acoustid_client_, SIGNAL(Finished(const int, const QStringList&, const QString&)), SLOT(PuidsFound(const int, const QStringList&, const QString&))); + connect(musicbrainz_client_, SIGNAL(Finished(const int, const MusicBrainzClient::ResultList&, const QString&)), SLOT(TagsFetched(const int, const MusicBrainzClient::ResultList&, const QString&))); } @@ -57,7 +57,7 @@ void TagFetcher::StartFetch(const SongList &songs) { QFuture future = QtConcurrent::mapped(songs_, GetFingerprint); fingerprint_watcher_ = new QFutureWatcher(this); fingerprint_watcher_->setFuture(future); - connect(fingerprint_watcher_, SIGNAL(resultReadyAt(int)), SLOT(FingerprintFound(int))); + connect(fingerprint_watcher_, SIGNAL(resultReadyAt(const int)), SLOT(FingerprintFound(const int))); for (const Song &song : songs) { emit Progress(song, tr("Fingerprinting song")); @@ -80,7 +80,7 @@ void TagFetcher::Cancel() { } -void TagFetcher::FingerprintFound(int index) { +void TagFetcher::FingerprintFound(const int index) { QFutureWatcher* watcher = reinterpret_cast*>(sender()); if (!watcher || index >= songs_.count()) { @@ -100,7 +100,7 @@ void TagFetcher::FingerprintFound(int index) { } -void TagFetcher::PuidsFound(int index, const QStringList &puid_list) { +void TagFetcher::PuidsFound(const int index, const QStringList &puid_list, const QString &error) { if (index >= songs_.count()) { return; @@ -109,7 +109,7 @@ void TagFetcher::PuidsFound(int index, const QStringList &puid_list) { const Song &song = songs_[index]; if (puid_list.isEmpty()) { - emit ResultAvailable(song, SongList()); + emit ResultAvailable(song, SongList(), error); return; } @@ -118,7 +118,7 @@ void TagFetcher::PuidsFound(int index, const QStringList &puid_list) { } -void TagFetcher::TagsFetched(int index, const MusicBrainzClient::ResultList &results) { +void TagFetcher::TagsFetched(const int index, const MusicBrainzClient::ResultList &results, const QString &error) { if (index >= songs_.count()) { return; @@ -135,7 +135,7 @@ void TagFetcher::TagsFetched(int index, const MusicBrainzClient::ResultList &res songs_guessed << song; } - emit ResultAvailable(original_song, songs_guessed); + emit ResultAvailable(original_song, songs_guessed, error); } diff --git a/src/musicbrainz/tagfetcher.h b/src/musicbrainz/tagfetcher.h index cbd240fa4..6d25838d1 100644 --- a/src/musicbrainz/tagfetcher.h +++ b/src/musicbrainz/tagfetcher.h @@ -46,14 +46,14 @@ class TagFetcher : public QObject { public slots: void Cancel(); -signals: + signals: void Progress(const Song &original_song, const QString &stage); - void ResultAvailable(const Song &original_song, const SongList &songs_guessed); + void ResultAvailable(const Song &original_song, const SongList &songs_guessed, const QString &error = QString()); private slots: - void FingerprintFound(int index); - void PuidsFound(int index, const QStringList &puid_list); - void TagsFetched(int index, const MusicBrainzClient::ResultList &result); + void FingerprintFound(const int index); + void PuidsFound(const int index, const QStringList &puid_list, const QString &error = QString()); + void TagsFetched(const int index, const MusicBrainzClient::ResultList &results, const QString &error = QString()); private: static QString GetFingerprint(const Song &song);