Keep track of some statistics while searching for album covers, and show a statistics dialog at the end. Fixes issue 1921

This commit is contained in:
David Sansome 2011-06-26 15:07:48 +00:00
parent 42801a967b
commit 7773e98ebf
18 changed files with 464 additions and 54 deletions

View File

@ -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

View File

@ -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());
}

View File

@ -18,6 +18,8 @@
#ifndef ALBUMCOVERFETCHER_H
#define ALBUMCOVERFETCHER_H
#include "coversearchstatistics.h"
#include <QHash>
#include <QImage>
#include <QList>
@ -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<CoverSearchResult> CoverSearchResults;
Q_DECLARE_METATYPE(QList<CoverSearchResult>);
// 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);

View File

@ -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 ; i<results_copy.count() ; ++i) {
if (results_copy[i].category.isEmpty()) {
results_copy[i].category = provider->name();
}
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 ; i<results_.count() ; ++i) {
if (results_[i].category == last_category) {
if (results_[i].provider == last_provider) {
continue;
}
CoverSearchResult result = results_.takeAt(i--);
last_category = result.category;
last_provider = result.provider;
qLog(Debug) << "Loading" << result.image_url << "from" << result.category;
qLog(Debug) << "Loading" << result.image_url << "from" << result.provider;
QNetworkReply* image_reply = network_->get(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<QNetworkReply*>(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();

View File

@ -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<int, CoverProvider*> pending_requests_;
QList<QNetworkReply*> pending_image_loads_;
QMap<QNetworkReply*, QString> pending_image_loads_;
NetworkTimeouts* image_load_timeout_;
// QMap happens to be sorted by key (score)
QMap<float, QImage> candidate_images_;
// QMap is sorted by key (score). Values are (provider_name, image)
typedef QPair<QString, QImage> CandidateImage;
QMap<float, CandidateImage> candidate_images_;
QNetworkAccessManager* network_;

View File

@ -0,0 +1,57 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#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_);
}

View File

@ -0,0 +1,43 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef COVERSEARCHSTATISTICS_H
#define COVERSEARCHSTATISTICS_H
#include <QMap>
#include <QString>
struct CoverSearchStatistics {
CoverSearchStatistics();
CoverSearchStatistics& operator +=(const CoverSearchStatistics& other);
quint64 network_requests_made_;
quint64 bytes_transferred_;
QMap<QString, quint64> total_images_by_provider_;
QMap<QString, quint64> chosen_images_by_provider_;
quint64 chosen_images_;
quint64 missing_images_;
quint64 chosen_width_;
quint64 chosen_height_;
QString AverageDimensions() const;
};
#endif // COVERSEARCHSTATISTICS_H

View File

@ -0,0 +1,96 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#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);
}

View File

@ -0,0 +1,47 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef COVERSEARCHSTATISTICSDIALOG_H
#define COVERSEARCHSTATISTICSDIALOG_H
#include <QDialog>
#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

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CoverSearchStatisticsDialog</class>
<widget class="QDialog" name="CoverSearchStatisticsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>425</width>
<height>214</height>
</rect>
</property>
<property name="windowTitle">
<string>Fetch completed</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="summary">
<property name="text">
<string>Got %1 covers out of %2 (%3 failed)</string>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="details">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>CoverSearchStatisticsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>CoverSearchStatisticsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -54,6 +54,11 @@
#include <taskmanager.h>
#include <urlhandler.h>
void PythonQtWrapper_AlbumCoverFetcherSearch::Cancel(AlbumCoverFetcherSearch* theWrappedObject)
{
( theWrappedObject->Cancel());
}
void PythonQtWrapper_AlbumCoverFetcherSearch::Start(AlbumCoverFetcherSearch* theWrappedObject)
{
( theWrappedObject->Start());

View File

@ -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; }

View File

@ -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);
}

View File

@ -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 ""

View File

@ -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 ""

View File

@ -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;
}
}

View File

@ -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<quint64, QListWidgetItem*> 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_;

View File

@ -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);