Load cover art for the library in a background thread. Fixes issue 2243

This commit is contained in:
David Sansome 2011-11-13 00:31:27 +00:00
parent 23bc7acaee
commit e9253af931
4 changed files with 63 additions and 38 deletions

View File

@ -224,7 +224,7 @@ void AlbumCoverLoader::RemoteFetchFinished() {
NextState(&task);
}
QImage AlbumCoverLoader::ScaleAndPad(const QImage &image) const {
QImage AlbumCoverLoader::ScaleAndPad(const QImage& image) const {
if (image.isNull())
return image;

View File

@ -49,6 +49,7 @@ using smart_playlists::QueryGenerator;
const char* LibraryModel::kSmartPlaylistsMimeType = "application/x-clementine-smart-playlist-generator";
const char* LibraryModel::kSmartPlaylistsSettingsGroup = "SerialisedSmartPlaylists";
const int LibraryModel::kSmartPlaylistsVersion = 4;
const int LibraryModel::kPrettyCoverSize = 32;
typedef QFuture<SqlRowList> RootQueryFuture;
typedef QFutureWatcher<SqlRowList> RootQueryWatcher;
@ -68,20 +69,28 @@ LibraryModel::LibraryModel(LibraryBackend* backend, TaskManager* task_manager,
playlists_dir_icon_(IconLoader::Load("folder-sound")),
playlist_icon_(":/icons/22x22/x-clementine-albums.png"),
init_task_id_(-1),
pretty_cover_size_(32, 32),
use_pretty_covers_(false),
show_dividers_(true)
show_dividers_(true),
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this))
{
root_->lazy_loaded = true;
group_by_[0] = GroupBy_Artist;
group_by_[1] = GroupBy_Album;
group_by_[2] = GroupBy_None;
no_cover_icon_pretty_ = QImage(":nocover.png").scaled(pretty_cover_size_,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
cover_loader_->Start(true);
cover_loader_->Worker()->SetDesiredHeight(kPrettyCoverSize);
cover_loader_->Worker()->SetPadOutputImage(true);
cover_loader_->Worker()->SetScaleOutputImage(true);
connect(cover_loader_->Worker().get(),
SIGNAL(ImageLoaded(quint64,QImage)),
SLOT(AlbumArtLoaded(quint64,QImage)));
no_cover_icon_pretty_ = QImage(":nocover.png").scaled(
kPrettyCoverSize, kPrettyCoverSize,
Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
LibraryModel::~LibraryModel() {
@ -378,35 +387,40 @@ void LibraryModel::SongsDeleted(const SongList& songs) {
}
}
QVariant LibraryModel::AlbumIcon(const QModelIndex& index, int role) const {
// the easiest way to get from *here* to working out what album art an index
// represents seems to be to get the node's child songs and look at their metadata.
// if none is found, return the generic CD icon
QVariant LibraryModel::AlbumIcon(const QModelIndex& index) {
// Cache the art in the item's metadata field
LibraryItem* item = IndexToItem(index);
if (!item)
return no_cover_icon_pretty_;
if (!item->metadata.image().isNull())
return item->metadata.image();
// No art is cached - load art for the first Song in the album.
SongList songs = GetChildSongs(index);
if (!songs.isEmpty()) {
const Song& s = songs.first();
QPixmap pixmap = AlbumCoverLoader::TryLoadPixmap(
s.art_automatic(), s.art_manual(), s.url().toLocalFile());
if (!pixmap.isNull()) {
QImage image = pixmap.toImage().scaled(
pretty_cover_size_, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
item->metadata.set_image(image);
return image;
}
const quint64 id = cover_loader_->Worker()->LoadImageAsync(songs.first());
pending_art_[id] = item;
}
return no_cover_icon_pretty_;
}
void LibraryModel::AlbumArtLoaded(quint64 id, const QImage& image) {
LibraryItem* item = pending_art_.take(id);
if (!item)
return;
if (image.isNull()) {
// Set the no_cover image so we don't continually try to load art.
item->metadata.set_image(no_cover_icon_pretty_);
} else {
item->metadata.set_image(image);
}
const QModelIndex index = ItemToIndex(item);
emit dataChanged(index, index);
}
QVariant LibraryModel::data(const QModelIndex& index, int role) const {
const LibraryItem* item = IndexToItem(index);
@ -415,15 +429,17 @@ QVariant LibraryModel::data(const QModelIndex& index, int role) const {
// QModelIndex& version of GetChildSongs, which satisfies const-ness, instead
// of the LibraryItem* version, which doesn't.
if (use_pretty_covers_) {
bool album_node = false;
bool is_album_node = false;
if (role == Qt::DecorationRole && item->type == LibraryItem::Type_Container) {
GroupBy container_type = group_by_[item->container_level];
album_node = container_type == GroupBy_Album
|| container_type == GroupBy_YearAlbum
|| container_type == GroupBy_AlbumArtist;
is_album_node = container_type == GroupBy_Album
|| container_type == GroupBy_YearAlbum
|| container_type == GroupBy_AlbumArtist;
}
if (is_album_node) {
// It has const behaviour some of the time - that's ok right?
return const_cast<LibraryModel*>(this)->AlbumIcon(index);
}
if (album_node)
return AlbumIcon(index, role);
}
return data(item, role);
@ -634,6 +650,7 @@ void LibraryModel::BeginReset() {
container_nodes_[1].clear();
container_nodes_[2].clear();
divider_nodes_.clear();
pending_art_.clear();
compilation_artist_node_ = NULL;
smart_playlist_node_ = NULL;
@ -1243,3 +1260,5 @@ void LibraryModel::TotalSongCountUpdatedSlot(int count) {
total_song_count_ = count;
emit TotalSongCountUpdated(count);
}

View File

@ -34,6 +34,7 @@
#include <boost/scoped_ptr.hpp>
class AlbumCoverLoader;
class LibraryDirectoryModel;
class LibraryBackend;
namespace smart_playlists { class Search; }
@ -53,6 +54,7 @@ class LibraryModel : public SimpleTreeModel<LibraryItem> {
static const char* kSmartPlaylistsSettingsGroup;
static const char* kSmartPlaylistsArray;
static const int kSmartPlaylistsVersion;
static const int kPrettyCoverSize;
enum Role {
Role_Type = Qt::UserRole + 1,
@ -165,6 +167,8 @@ class LibraryModel : public SimpleTreeModel<LibraryItem> {
// Called after ResetAsync
void ResetAsyncQueryFinished();
void AlbumArtLoaded(quint64 id, const QImage& image);
private:
// Provides some optimisations for loading the list of items in the root.
// This gets called a lot when filtering the playlist, so it's nice to be
@ -218,7 +222,7 @@ class LibraryModel : public SimpleTreeModel<LibraryItem> {
QString DividerDisplayText(GroupBy type, const QString& key) const;
// Helpers
QVariant AlbumIcon(const QModelIndex& index, int role) const;
QVariant AlbumIcon(const QModelIndex& index);
QVariant data(const LibraryItem* item, int role) const;
bool CompareItems(const LibraryItem* a, const LibraryItem* b) const;
@ -261,9 +265,11 @@ class LibraryModel : public SimpleTreeModel<LibraryItem> {
int init_task_id_;
QSize pretty_cover_size_;
bool use_pretty_covers_;
bool show_dividers_;
BackgroundThread<AlbumCoverLoader>* cover_loader_;
QMap<quint64, LibraryItem*> pending_art_;
};
Q_DECLARE_METATYPE(LibraryModel::Grouping);

View File

@ -2356,7 +2356,7 @@ msgstr ""
msgid "Loading smart playlist"
msgstr ""
#: library/librarymodel.cpp:122
#: library/librarymodel.cpp:131
msgid "Loading songs"
msgstr ""
@ -2373,7 +2373,7 @@ msgstr ""
msgid "Loading tracks info"
msgstr ""
#: library/librarymodel.cpp:117 widgets/prettyimage.cpp:168
#: library/librarymodel.cpp:126 widgets/prettyimage.cpp:168
#: widgets/widgetfadehelper.cpp:93 ../bin/src/ui_searchpreview.h:106
msgid "Loading..."
msgstr ""
@ -3650,7 +3650,7 @@ msgstr ""
msgid "Smart playlist"
msgstr ""
#: library/librarymodel.cpp:1101
#: library/librarymodel.cpp:1118
msgid "Smart playlists"
msgstr ""
@ -4091,8 +4091,8 @@ msgid "Unable to download %1 (%2)"
msgstr ""
#: core/song.cpp:148 globalsearch/globalsearchitemdelegate.cpp:166
#: globalsearch/globalsearchitemdelegate.cpp:173 library/librarymodel.cpp:284
#: library/librarymodel.cpp:289 library/librarymodel.cpp:899
#: globalsearch/globalsearchitemdelegate.cpp:173 library/librarymodel.cpp:293
#: library/librarymodel.cpp:298 library/librarymodel.cpp:916
#: playlist/playlistdelegates.cpp:306 playlist/playlistmanager.cpp:381
#: playlist/playlistmanager.cpp:384 ui/albumcoverchoicecontroller.cpp:111
msgid "Unknown"
@ -4209,7 +4209,7 @@ msgstr ""
msgid "Variable bit rate"
msgstr ""
#: globalsearch/globalsearchitemdelegate.cpp:162 library/librarymodel.cpp:221
#: globalsearch/globalsearchitemdelegate.cpp:162 library/librarymodel.cpp:230
#: playlist/playlistmanager.cpp:396 ui/albumcovermanager.cpp:264
msgid "Various artists"
msgstr ""