diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 651f0486e..cdaabf11b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -100,6 +100,8 @@ set(SOURCES covers/artloader.cpp covers/coverprovider.cpp covers/coverproviders.cpp + covers/coversearchstatistics.cpp + covers/coversearchstatisticsdialog.cpp covers/kittenloader.cpp devices/connecteddevice.cpp @@ -329,6 +331,7 @@ set(HEADERS covers/artloader.h covers/coverprovider.h covers/coverproviders.h + covers/coversearchstatisticsdialog.h covers/kittenloader.h devices/connecteddevice.h @@ -499,6 +502,8 @@ set(HEADERS ) set(UI + covers/coversearchstatisticsdialog.ui + devices/deviceproperties.ui library/groupbydialog.ui diff --git a/src/covers/albumcoverfetcher.cpp b/src/covers/albumcoverfetcher.cpp index 472f0f34a..ac3197c77 100644 --- a/src/covers/albumcoverfetcher.cpp +++ b/src/covers/albumcoverfetcher.cpp @@ -23,6 +23,7 @@ const int AlbumCoverFetcher::kMaxConcurrentRequests = 5; + AlbumCoverFetcher::AlbumCoverFetcher(QObject* parent, QNetworkAccessManager* network) : QObject(parent), network_(network ? network : new NetworkAccessManager(this)), @@ -100,16 +101,17 @@ void AlbumCoverFetcher::StartRequests() { SLOT(SingleCoverFetched(quint64, const QImage&))); search->Start(); - } } void AlbumCoverFetcher::SingleSearchFinished(quint64 request_id, CoverSearchResults results) { - active_requests_.take(request_id)->deleteLater(); - emit SearchFinished(request_id, results); + AlbumCoverFetcherSearch* search = active_requests_.take(request_id); + search->deleteLater(); + emit SearchFinished(request_id, results, search->statistics()); } void AlbumCoverFetcher::SingleCoverFetched(quint64 request_id, const QImage& image) { - active_requests_.take(request_id)->deleteLater(); - emit AlbumCoverFetched(request_id, image); + AlbumCoverFetcherSearch* search = active_requests_.take(request_id); + search->deleteLater(); + emit AlbumCoverFetched(request_id, image, search->statistics()); } diff --git a/src/covers/albumcoverfetcher.h b/src/covers/albumcoverfetcher.h index d89dc1d85..1845c9276 100644 --- a/src/covers/albumcoverfetcher.h +++ b/src/covers/albumcoverfetcher.h @@ -18,6 +18,8 @@ #ifndef ALBUMCOVERFETCHER_H #define ALBUMCOVERFETCHER_H +#include "coversearchstatistics.h" + #include #include #include @@ -52,9 +54,9 @@ struct CoverSearchRequest { // It contains an URL that leads to a found cover plus it's description (usually // the "artist - album" string). struct CoverSearchResult { - // used for grouping in the user interface. defaults to the name of the - // provider that this result came from. - QString category; + // used for grouping in the user interface. This is set automatically - don't + // set it manually in your cover provider. + QString provider; // description of this result (we suggest using the "artist - album" format) QString description; @@ -64,11 +66,13 @@ struct CoverSearchResult { }; Q_DECLARE_METATYPE(CoverSearchResult); + // This is a complete result of a single search request (a list of results, each // describing one image, actually). typedef QList CoverSearchResults; Q_DECLARE_METATYPE(QList); + // This class searches for album covers for a given query or artist/album and // returns URLs. It's NOT thread-safe. class AlbumCoverFetcher : public QObject { @@ -86,8 +90,10 @@ class AlbumCoverFetcher : public QObject { void Clear(); signals: - void AlbumCoverFetched(quint64, const QImage& cover); - void SearchFinished(quint64, const CoverSearchResults& results); + void AlbumCoverFetched(quint64, const QImage& cover, + const CoverSearchStatistics& statistics); + void SearchFinished(quint64, const CoverSearchResults& results, + const CoverSearchStatistics& statistics); private slots: void SingleSearchFinished(quint64, CoverSearchResults results); diff --git a/src/covers/albumcoverfetchersearch.cpp b/src/covers/albumcoverfetchersearch.cpp index f9f9878d9..90ee0ecd1 100644 --- a/src/covers/albumcoverfetchersearch.cpp +++ b/src/covers/albumcoverfetchersearch.cpp @@ -68,6 +68,7 @@ void AlbumCoverFetcherSearch::Start() { if (success) { pending_requests_[id] = provider; + statistics_.network_requests_made_ ++; } } @@ -77,9 +78,9 @@ void AlbumCoverFetcherSearch::Start() { } } -static bool CompareCategories(const CoverSearchResult& a, - const CoverSearchResult& b) { - return a.category < b.category; +static bool CompareProviders(const CoverSearchResult& a, + const CoverSearchResult& b) { + return a.provider < b.provider; } void AlbumCoverFetcherSearch::ProviderSearchFinished( @@ -90,15 +91,14 @@ void AlbumCoverFetcherSearch::ProviderSearchFinished( CoverProvider* provider = pending_requests_.take(id); CoverSearchResults results_copy(results); - // Add categories to the results if the provider didn't specify them + // Set categories on the results for (int i=0 ; iname(); - } + results_copy[i].provider = provider->name(); } // Add results from the current provider to our pool results_.append(results_copy); + statistics_.total_images_by_provider_[provider->name()] ++; // do we have more providers left? if(!pending_requests_.isEmpty()) { @@ -121,6 +121,7 @@ void AlbumCoverFetcherSearch::AllProvidersFinished() { // no results? if (results_.isEmpty()) { + statistics_.missing_images_ ++; emit AlbumCoverFetched(request_.id, QImage()); return; } @@ -130,27 +131,29 @@ void AlbumCoverFetcherSearch::AllProvidersFinished() { // from each category and use some heuristics to score them. If no images // are good enough we'll keep loading more images until we find one that is // or we run out of results. - qStableSort(results_.begin(), results_.end(), CompareCategories); + qStableSort(results_.begin(), results_.end(), CompareProviders); FetchMoreImages(); } void AlbumCoverFetcherSearch::FetchMoreImages() { // Try the first one in each category. - QString last_category; + QString last_provider; for (int i=0 ; iget(QNetworkRequest(result.image_url)); connect(image_reply, SIGNAL(finished()), SLOT(ProviderCoverFetchFinished())); - pending_image_loads_ << image_reply; + pending_image_loads_[image_reply] = result.provider; image_load_timeout_->AddReply(image_reply); + + statistics_.network_requests_made_ ++; } if (pending_image_loads_.isEmpty()) { @@ -162,7 +165,9 @@ void AlbumCoverFetcherSearch::FetchMoreImages() { void AlbumCoverFetcherSearch::ProviderCoverFetchFinished() { QNetworkReply* reply = qobject_cast(sender()); reply->deleteLater(); - pending_image_loads_.removeAll(reply); + const QString provider = pending_image_loads_.take(reply); + + statistics_.bytes_transferred_ += reply->bytesAvailable(); if (cancel_requested_) { return; @@ -176,7 +181,7 @@ void AlbumCoverFetcherSearch::ProviderCoverFetchFinished() { qLog(Info) << "Error decoding image data from" << reply->url(); } else { const float score = ScoreImage(image); - candidate_images_.insertMulti(score, image); + candidate_images_.insertMulti(score, CandidateImage(provider, image)); qLog(Debug) << reply->url() << "scored" << score; } @@ -220,7 +225,15 @@ void AlbumCoverFetcherSearch::SendBestImage() { QImage image; if (!candidate_images_.isEmpty()) { - image = candidate_images_.values().back(); + const CandidateImage best_image = candidate_images_.values().back(); + image = best_image.second; + + statistics_.chosen_images_by_provider_[best_image.first] ++; + statistics_.chosen_images_ ++; + statistics_.chosen_width_ += image.width(); + statistics_.chosen_height_ += image.height(); + } else { + statistics_.missing_images_ ++; } emit AlbumCoverFetched(request_.id, image); @@ -232,7 +245,7 @@ void AlbumCoverFetcherSearch::Cancel() { if (!pending_requests_.isEmpty()) { TerminateSearch(); } else if (!pending_image_loads_.isEmpty()) { - foreach (QNetworkReply* reply, pending_image_loads_) { + foreach (QNetworkReply* reply, pending_image_loads_.keys()) { reply->abort(); } pending_image_loads_.clear(); diff --git a/src/covers/albumcoverfetchersearch.h b/src/covers/albumcoverfetchersearch.h index 5c0917395..3f45cd29c 100644 --- a/src/covers/albumcoverfetchersearch.h +++ b/src/covers/albumcoverfetchersearch.h @@ -45,9 +45,12 @@ class AlbumCoverFetcherSearch : public QObject { // is the caller's responsibility to delete the AlbumCoverFetcherSearch. void Cancel(); + CoverSearchStatistics statistics() const { return statistics_; } + signals: // It's the end of search (when there was no fetch-me-a-cover request). void SearchFinished(quint64, const CoverSearchResults& results); + // It's the end of search and we've fetched a cover. void AlbumCoverFetched(quint64, const QImage& cover); @@ -69,6 +72,8 @@ private: static const int kTargetSize; static const float kGoodScore; + CoverSearchStatistics statistics_; + // Search request encapsulated by this AlbumCoverFetcherSearch. CoverSearchRequest request_; @@ -76,11 +81,12 @@ private: CoverSearchResults results_; QMap pending_requests_; - QList pending_image_loads_; + QMap pending_image_loads_; NetworkTimeouts* image_load_timeout_; - // QMap happens to be sorted by key (score) - QMap candidate_images_; + // QMap is sorted by key (score). Values are (provider_name, image) + typedef QPair CandidateImage; + QMap candidate_images_; QNetworkAccessManager* network_; diff --git a/src/covers/coversearchstatistics.cpp b/src/covers/coversearchstatistics.cpp new file mode 100644 index 000000000..5a3bae616 --- /dev/null +++ b/src/covers/coversearchstatistics.cpp @@ -0,0 +1,57 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "coversearchstatistics.h" + +CoverSearchStatistics::CoverSearchStatistics() + : network_requests_made_(0), + bytes_transferred_(0), + chosen_images_(0), + missing_images_(0), + chosen_width_(0), + chosen_height_(0) +{ +} + +CoverSearchStatistics& CoverSearchStatistics::operator +=(const CoverSearchStatistics& other) { + network_requests_made_ += other.network_requests_made_; + bytes_transferred_ += other.bytes_transferred_; + + foreach (const QString& key, other.chosen_images_by_provider_.keys()) { + chosen_images_by_provider_[key] += other.chosen_images_by_provider_[key]; + } + foreach (const QString& key, other.total_images_by_provider_.keys()) { + total_images_by_provider_[key] += other.total_images_by_provider_[key]; + } + + chosen_images_ += other.chosen_images_; + missing_images_ += other.missing_images_; + + chosen_width_ += other.chosen_width_; + chosen_height_ += other.chosen_height_; + + return *this; +} + +QString CoverSearchStatistics::AverageDimensions() const { + if (chosen_images_ == 0) { + return "0x0"; + } + + return QString::number(chosen_width_ / chosen_images_) + "x" + + QString::number(chosen_height_ / chosen_images_); +} diff --git a/src/covers/coversearchstatistics.h b/src/covers/coversearchstatistics.h new file mode 100644 index 000000000..90b4b67d8 --- /dev/null +++ b/src/covers/coversearchstatistics.h @@ -0,0 +1,43 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef COVERSEARCHSTATISTICS_H +#define COVERSEARCHSTATISTICS_H + +#include +#include + +struct CoverSearchStatistics { + CoverSearchStatistics(); + + CoverSearchStatistics& operator +=(const CoverSearchStatistics& other); + + quint64 network_requests_made_; + quint64 bytes_transferred_; + QMap total_images_by_provider_; + QMap chosen_images_by_provider_; + + quint64 chosen_images_; + quint64 missing_images_; + + quint64 chosen_width_; + quint64 chosen_height_; + + QString AverageDimensions() const; +}; + +#endif // COVERSEARCHSTATISTICS_H diff --git a/src/covers/coversearchstatisticsdialog.cpp b/src/covers/coversearchstatisticsdialog.cpp new file mode 100644 index 000000000..449ad2f8a --- /dev/null +++ b/src/covers/coversearchstatisticsdialog.cpp @@ -0,0 +1,96 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "coversearchstatisticsdialog.h" +#include "ui_coversearchstatisticsdialog.h" +#include "core/utilities.h" + +CoverSearchStatisticsDialog::CoverSearchStatisticsDialog(QWidget *parent) + : QDialog(parent), + ui_(new Ui_CoverSearchStatisticsDialog) +{ + ui_->setupUi(this); + details_layout_ = new QVBoxLayout(ui_->details); + details_layout_->setSpacing(0); + + setStyleSheet( + "#details {" + " background-color: palette(base);" + "}" + "#details QLabel[type=\"label\"] {" + " border: 2px solid transparent;" + " border-right: 2px solid palette(midlight);" + " margin-right: 10px;" + "}" + "#details QLabel[type=\"value\"] {" + " font-weight: bold;" + " max-width: 100px;" + "}" + ); +} + +CoverSearchStatisticsDialog::~CoverSearchStatisticsDialog() { + delete ui_; +} + +void CoverSearchStatisticsDialog::Show(const CoverSearchStatistics& statistics) { + QStringList providers(statistics.total_images_by_provider_.keys()); + qSort(providers); + + ui_->summary->setText(tr("Got %1 covers out of %2 (%3 failed)") + .arg(statistics.chosen_images_) + .arg(statistics.chosen_images_ + statistics.missing_images_) + .arg(statistics.missing_images_)); + + foreach (const QString& provider, providers) { + AddLine(tr("Covers from %1").arg(provider), + QString::number(statistics.chosen_images_by_provider_[provider])); + } + + if (!providers.isEmpty()) { + AddSpacer(); + } + + AddLine(tr("Total network requests made"), + QString::number(statistics.network_requests_made_)); + AddLine(tr("Average image size"), statistics.AverageDimensions()); + AddLine(tr("Total bytes transferred"), + statistics.bytes_transferred_ + ? Utilities::PrettySize(statistics.bytes_transferred_) + : "0 bytes"); + + details_layout_->addStretch(); + + show(); +} + +void CoverSearchStatisticsDialog::AddLine(const QString& label, const QString& value) { + QLabel* label1 = new QLabel(label); + QLabel* label2 = new QLabel(value); + + label1->setProperty("type", "label"); + label2->setProperty("type", "value"); + + QHBoxLayout* layout = new QHBoxLayout; + layout->addWidget(label1); + layout->addWidget(label2); + details_layout_->addLayout(layout); +} + +void CoverSearchStatisticsDialog::AddSpacer() { + details_layout_->addSpacing(20); +} diff --git a/src/covers/coversearchstatisticsdialog.h b/src/covers/coversearchstatisticsdialog.h new file mode 100644 index 000000000..6f53e0b18 --- /dev/null +++ b/src/covers/coversearchstatisticsdialog.h @@ -0,0 +1,47 @@ +/* This file is part of Clementine. + Copyright 2010, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef COVERSEARCHSTATISTICSDIALOG_H +#define COVERSEARCHSTATISTICSDIALOG_H + +#include + +#include "coversearchstatistics.h" + +class Ui_CoverSearchStatisticsDialog; + +class QVBoxLayout; + +class CoverSearchStatisticsDialog : public QDialog { + Q_OBJECT + +public: + CoverSearchStatisticsDialog(QWidget* parent = 0); + ~CoverSearchStatisticsDialog(); + + void Show(const CoverSearchStatistics& statistics); + +private: + void AddLine(const QString& label, const QString& value); + void AddSpacer(); + +private: + Ui_CoverSearchStatisticsDialog* ui_; + QVBoxLayout* details_layout_; +}; + +#endif // COVERSEARCHSTATISTICSDIALOG_H diff --git a/src/covers/coversearchstatisticsdialog.ui b/src/covers/coversearchstatisticsdialog.ui new file mode 100644 index 000000000..6f8a08d59 --- /dev/null +++ b/src/covers/coversearchstatisticsdialog.ui @@ -0,0 +1,87 @@ + + + CoverSearchStatisticsDialog + + + + 0 + 0 + 425 + 214 + + + + Fetch completed + + + + + + Got %1 covers out of %2 (%3 failed) + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox + accepted() + CoverSearchStatisticsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CoverSearchStatisticsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/generated_cpp/clementine/clementine0.cpp b/src/generated_cpp/clementine/clementine0.cpp index 185c00a5e..7f6e15abd 100644 --- a/src/generated_cpp/clementine/clementine0.cpp +++ b/src/generated_cpp/clementine/clementine0.cpp @@ -54,6 +54,11 @@ #include #include +void PythonQtWrapper_AlbumCoverFetcherSearch::Cancel(AlbumCoverFetcherSearch* theWrappedObject) +{ + ( theWrappedObject->Cancel()); +} + void PythonQtWrapper_AlbumCoverFetcherSearch::Start(AlbumCoverFetcherSearch* theWrappedObject) { ( theWrappedObject->Start()); diff --git a/src/generated_cpp/clementine/clementine0.h b/src/generated_cpp/clementine/clementine0.h index ce20687b2..5fe93b2fc 100644 --- a/src/generated_cpp/clementine/clementine0.h +++ b/src/generated_cpp/clementine/clementine0.h @@ -61,6 +61,7 @@ class PythonQtWrapper_AlbumCoverFetcherSearch : public QObject public: public slots: void delete_AlbumCoverFetcherSearch(AlbumCoverFetcherSearch* obj) { delete obj; } + void Cancel(AlbumCoverFetcherSearch* theWrappedObject); void Start(AlbumCoverFetcherSearch* theWrappedObject); }; @@ -138,8 +139,8 @@ PythonQtShell_CoverSearchResult* a = new PythonQtShell_CoverSearchResult(); *((CoverSearchResult*)a) = other; return a; } void delete_CoverSearchResult(CoverSearchResult* obj) { delete obj; } -void py_set_category(CoverSearchResult* theWrappedObject, QString category){ theWrappedObject->category = category; } -QString py_get_category(CoverSearchResult* theWrappedObject){ return theWrappedObject->category; } +void py_set_provider(CoverSearchResult* theWrappedObject, QString provider){ theWrappedObject->provider = provider; } +QString py_get_provider(CoverSearchResult* theWrappedObject){ return theWrappedObject->provider; } void py_set_description(CoverSearchResult* theWrappedObject, QString description){ theWrappedObject->description = description; } QString py_get_description(CoverSearchResult* theWrappedObject){ return theWrappedObject->description; } void py_set_image_url(CoverSearchResult* theWrappedObject, QString image_url){ theWrappedObject->image_url = image_url; } diff --git a/src/scripting/python/pythonscript.cpp b/src/scripting/python/pythonscript.cpp index 65489c649..308632514 100644 --- a/src/scripting/python/pythonscript.cpp +++ b/src/scripting/python/pythonscript.cpp @@ -80,7 +80,6 @@ bool PythonScript::Unload() { // running. This is important because those connections will hold references // to bound methods in the script's classes, so the classes won't get deleted. foreach (const SignalConnection& conn, signal_connections_) { - qLog(Debug) << "Disconnecting signal" << conn.signal_id_; conn.receiver_->removeSignalHandler(conn.signal_id_, conn.callable_); } @@ -120,6 +119,5 @@ bool PythonScript::Unload() { void PythonScript::RegisterSignalConnection(PythonQtSignalReceiver* receiver, int signal_id, PyObject* callable) { - qLog(Debug) << "Signal" << signal_id << "registered to an object in" << info().id(); signal_connections_ << SignalConnection(receiver, signal_id, callable); } diff --git a/src/translations/en.po b/src/translations/en.po index 070ed616d..200cd1fc0 100644 --- a/src/translations/en.po +++ b/src/translations/en.po @@ -73,6 +73,10 @@ msgstr "" msgid "%1 tracks" msgstr "" +#, qt-format +msgid "%1 transferred" +msgstr "" + #, qt-format msgid "%1: Wiimotedev module" msgstr "" @@ -472,6 +476,9 @@ msgstr "" msgid "Average bitrate" msgstr "" +msgid "Average image size" +msgstr "" + msgid "BPM" msgstr "" @@ -1186,6 +1193,9 @@ msgstr "" msgid "Fetch automatically" msgstr "" +msgid "Fetch completed" +msgstr "" + msgid "Fetching cover error" msgstr "" @@ -2834,6 +2844,12 @@ msgstr "" msgid "Toggle visibility for the pretty on-screen-display" msgstr "" +msgid "Total bytes transferred" +msgstr "" + +msgid "Total network requests made" +msgstr "" + msgid "Track" msgstr "" diff --git a/src/translations/translations.pot b/src/translations/translations.pot index 2ae7adf23..040d94f57 100644 --- a/src/translations/translations.pot +++ b/src/translations/translations.pot @@ -63,6 +63,10 @@ msgstr "" msgid "%1 tracks" msgstr "" +#, qt-format +msgid "%1 transferred" +msgstr "" + #, qt-format msgid "%1: Wiimotedev module" msgstr "" @@ -462,6 +466,9 @@ msgstr "" msgid "Average bitrate" msgstr "" +msgid "Average image size" +msgstr "" + msgid "BPM" msgstr "" @@ -1176,6 +1183,9 @@ msgstr "" msgid "Fetch automatically" msgstr "" +msgid "Fetch completed" +msgstr "" + msgid "Fetching cover error" msgstr "" @@ -2824,6 +2834,12 @@ msgstr "" msgid "Toggle visibility for the pretty on-screen-display" msgstr "" +msgid "Total bytes transferred" +msgstr "" + +msgid "Total network requests made" +msgstr "" + msgid "Track" msgstr "" diff --git a/src/ui/albumcovermanager.cpp b/src/ui/albumcovermanager.cpp index 47201641f..8bb824279 100644 --- a/src/ui/albumcovermanager.cpp +++ b/src/ui/albumcovermanager.cpp @@ -19,8 +19,10 @@ #include "albumcoversearcher.h" #include "iconloader.h" #include "ui_albumcovermanager.h" +#include "core/utilities.h" #include "covers/albumcoverfetcher.h" #include "covers/coverproviders.h" +#include "covers/coversearchstatisticsdialog.h" #include "library/librarybackend.h" #include "library/libraryquery.h" #include "library/sqlrow.h" @@ -59,9 +61,7 @@ AlbumCoverManager::AlbumCoverManager(LibraryBackend* backend, QWidget* parent, all_artists_icon_(IconLoader::Load("x-clementine-album")), context_menu_(new QMenu(this)), progress_bar_(new QProgressBar(this)), - jobs_(0), - got_covers_(0), - missing_covers_(0) + jobs_(0) { ui_->setupUi(this); ui_->albums->set_cover_manager(this); @@ -155,8 +155,8 @@ void AlbumCoverManager::Init() { connect(filter_group, SIGNAL(triggered(QAction*)), SLOT(UpdateFilter())); connect(ui_->view, SIGNAL(clicked()), ui_->view, SLOT(showMenu())); connect(ui_->fetch, SIGNAL(clicked()), SLOT(FetchAlbumCovers())); - connect(cover_fetcher_, SIGNAL(AlbumCoverFetched(quint64,QImage)), - SLOT(AlbumCoverFetched(quint64,QImage))); + connect(cover_fetcher_, SIGNAL(AlbumCoverFetched(quint64,QImage,CoverSearchStatistics)), + SLOT(AlbumCoverFetched(quint64,QImage,CoverSearchStatistics))); connect(ui_->action_fetch, SIGNAL(triggered()), SLOT(FetchSingleCover())); connect(ui_->albums, SIGNAL(doubleClicked(QModelIndex)), SLOT(AlbumDoubleClicked(QModelIndex))); connect(ui_->action_add_to_playlist, SIGNAL(triggered()), SLOT(AddSelectedToPlaylist())); @@ -384,19 +384,17 @@ void AlbumCoverManager::FetchAlbumCovers() { progress_bar_->setMaximum(jobs_); progress_bar_->show(); + fetch_statistics_ = CoverSearchStatistics(); UpdateStatusText(); } -void AlbumCoverManager::AlbumCoverFetched(quint64 id, const QImage &image) { +void AlbumCoverManager::AlbumCoverFetched(quint64 id, const QImage& image, + const CoverSearchStatistics& statistics) { if (!cover_fetching_tasks_.contains(id)) return; QListWidgetItem* item = cover_fetching_tasks_.take(id); - if (image.isNull()) { - missing_covers_ ++; - } else { - got_covers_ ++; - + if (!image.isNull()) { SaveAndSetCover(item, image); } @@ -404,22 +402,34 @@ void AlbumCoverManager::AlbumCoverFetched(quint64 id, const QImage &image) { ResetFetchCoversButton(); } + fetch_statistics_ += statistics; UpdateStatusText(); } void AlbumCoverManager::UpdateStatusText() { QString message = tr("Got %1 covers out of %2 (%3 failed)") - .arg(got_covers_).arg(jobs_).arg(missing_covers_); + .arg(fetch_statistics_.chosen_images_) + .arg(jobs_) + .arg(fetch_statistics_.missing_images_); + + if (fetch_statistics_.bytes_transferred_) { + message += ", " + tr("%1 transferred") + .arg(Utilities::PrettySize(fetch_statistics_.bytes_transferred_)); + } + statusBar()->showMessage(message); - progress_bar_->setValue(got_covers_ + missing_covers_); + progress_bar_->setValue(fetch_statistics_.chosen_images_ + + fetch_statistics_.missing_images_); if (cover_fetching_tasks_.isEmpty()) { QTimer::singleShot(2000, statusBar(), SLOT(clearMessage())); progress_bar_->hide(); + CoverSearchStatisticsDialog* dialog = new CoverSearchStatisticsDialog(this); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->Show(fetch_statistics_); + jobs_ = 0; - got_covers_ = 0; - missing_covers_ = 0; } } diff --git a/src/ui/albumcovermanager.h b/src/ui/albumcovermanager.h index 71dbd9e6a..76ff5b7a9 100644 --- a/src/ui/albumcovermanager.h +++ b/src/ui/albumcovermanager.h @@ -27,6 +27,7 @@ #include "core/backgroundthread.h" #include "core/song.h" #include "covers/albumcoverloader.h" +#include "covers/coversearchstatistics.h" class AlbumCoverChoiceController; class AlbumCoverFetcher; @@ -78,7 +79,8 @@ class AlbumCoverManager : public QMainWindow { void CoverImageLoaded(quint64 id, const QImage& image); void UpdateFilter(); void FetchAlbumCovers(); - void AlbumCoverFetched(quint64 id, const QImage& image); + void AlbumCoverFetched(quint64 id, const QImage& image, + const CoverSearchStatistics& statistics); // On the context menu void FetchSingleCover(); @@ -152,6 +154,7 @@ class AlbumCoverManager : public QMainWindow { AlbumCoverFetcher* cover_fetcher_; QMap cover_fetching_tasks_; + CoverSearchStatistics fetch_statistics_; AlbumCoverSearcher* cover_searcher_; @@ -165,8 +168,6 @@ class AlbumCoverManager : public QMainWindow { QProgressBar* progress_bar_; int jobs_; - int got_covers_; - int missing_covers_; LineEditInterface* filter_; diff --git a/src/ui/albumcoversearcher.cpp b/src/ui/albumcoversearcher.cpp index 466d4793a..65def0088 100644 --- a/src/ui/albumcoversearcher.cpp +++ b/src/ui/albumcoversearcher.cpp @@ -64,7 +64,8 @@ AlbumCoverSearcher::~AlbumCoverSearcher() { void AlbumCoverSearcher::Init(AlbumCoverFetcher* fetcher) { fetcher_ = fetcher; - connect(fetcher_, SIGNAL(SearchFinished(quint64,CoverSearchResults)), SLOT(SearchFinished(quint64,CoverSearchResults))); + connect(fetcher_, SIGNAL(SearchFinished(quint64,CoverSearchResults,CoverSearchStatistics)), + SLOT(SearchFinished(quint64,CoverSearchResults))); } QImage AlbumCoverSearcher::Exec(const QString& artist, const QString& album) { @@ -126,7 +127,7 @@ void AlbumCoverSearcher::SearchFinished(quint64 id, const CoverSearchResults& re item->setData(id, Role_ImageRequestId); item->setData(false, Role_ImageFetchFinished); item->setData(QVariant(Qt::AlignTop | Qt::AlignHCenter), Qt::TextAlignmentRole); - item->setData(result.category, GroupedIconView::Role_Group); + item->setData(result.provider, GroupedIconView::Role_Group); model_->appendRow(item);