Improve album cover loader, lyrics search and streaming support
- Improve album cover loader - Add album cover loader result struct - Move album cover thumbnail scaling to album cover loader - Make init art manual look for album cover images in song directory - Make album cover search work for songs outside of collection and streams - Make album cover search work based on artist + title if album is not present - Update art manual in playlist for local files, devices and CDDA - Make lyrics search work for streams - Add stream dialog to menu - Remove dead code in InternetSearchModel - Simplify code in InternetSearchView
This commit is contained in:
@@ -46,9 +46,11 @@ InternetPlaylistItem::InternetPlaylistItem(InternetService *service, const Song
|
||||
}
|
||||
|
||||
bool InternetPlaylistItem::InitFromQuery(const SqlRow &query) {
|
||||
|
||||
metadata_.InitFromQuery(query, false, (Song::kColumns.count() + 1) * 1);
|
||||
InitMetadata();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
QVariant InternetPlaylistItem::DatabaseValue(DatabaseColumn column) const {
|
||||
@@ -56,15 +58,26 @@ QVariant InternetPlaylistItem::DatabaseValue(DatabaseColumn column) const {
|
||||
}
|
||||
|
||||
void InternetPlaylistItem::InitMetadata() {
|
||||
|
||||
if (metadata_.title().isEmpty()) metadata_.set_title(metadata_.url().toString());
|
||||
if (metadata_.source() == Song::Source_Unknown) metadata_.set_source(Song::Source_Stream);
|
||||
if (metadata_.filetype() == Song::FileType_Unknown) metadata_.set_filetype(Song::FileType_Stream);
|
||||
metadata_.set_valid(true);
|
||||
|
||||
}
|
||||
|
||||
Song InternetPlaylistItem::Metadata() const {
|
||||
|
||||
if (HasTemporaryMetadata()) return temp_metadata_;
|
||||
return metadata_;
|
||||
|
||||
}
|
||||
|
||||
QUrl InternetPlaylistItem::Url() const { return metadata_.url(); }
|
||||
|
||||
void InternetPlaylistItem::SetArtManual(const QUrl &cover_url) {
|
||||
|
||||
metadata_.set_art_manual(cover_url);
|
||||
temp_metadata_.set_art_manual(cover_url);
|
||||
|
||||
}
|
||||
|
||||
@@ -36,10 +36,11 @@ class InternetPlaylistItem : public PlaylistItem {
|
||||
|
||||
public:
|
||||
explicit InternetPlaylistItem(const Song::Source &type);
|
||||
InternetPlaylistItem(InternetService *service, const Song &metadata);
|
||||
explicit InternetPlaylistItem(InternetService *service, const Song &metadata);
|
||||
bool InitFromQuery(const SqlRow &query);
|
||||
Song Metadata() const;
|
||||
QUrl Url() const;
|
||||
void SetArtManual(const QUrl &cover_url);
|
||||
|
||||
protected:
|
||||
QVariant DatabaseValue(DatabaseColumn) const;
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
InternetSearchItemDelegate::InternetSearchItemDelegate(InternetSearchView *view)
|
||||
: CollectionItemDelegate(view), view_(view) {}
|
||||
|
||||
void InternetSearchItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
|
||||
void InternetSearchItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const {
|
||||
|
||||
// Tell the view we painted this item so it can lazy load some art.
|
||||
const_cast<InternetSearchView*>(view_)->LazyLoadAlbumCover(index);
|
||||
const_cast<InternetSearchView*>(view_)->LazyLoadAlbumCover(idx);
|
||||
|
||||
CollectionItemDelegate::paint(painter, option, index);
|
||||
CollectionItemDelegate::paint(painter, option, idx);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This code was part of Clementine (GlobalSearch)
|
||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -56,21 +57,17 @@ InternetSearchModel::InternetSearchModel(InternetService *service, QObject *pare
|
||||
|
||||
void InternetSearchModel::AddResults(const InternetSearchView::ResultList &results) {
|
||||
|
||||
int sort_index = 0;
|
||||
|
||||
for (const InternetSearchView::Result &result : results) {
|
||||
QStandardItem *parent = invisibleRootItem();
|
||||
|
||||
// Find (or create) the container nodes for this result if we can.
|
||||
ContainerKey key;
|
||||
key.provider_index_ = sort_index;
|
||||
parent = BuildContainers(result.metadata_, parent, &key);
|
||||
|
||||
// Create the item
|
||||
QStandardItem *item = new QStandardItem;
|
||||
item->setText(result.metadata_.TitleWithCompilationArtist());
|
||||
item->setData(QVariant::fromValue(result), Role_Result);
|
||||
item->setData(sort_index, Role_ProviderIndex);
|
||||
|
||||
parent->appendRow(item);
|
||||
|
||||
@@ -78,7 +75,7 @@ void InternetSearchModel::AddResults(const InternetSearchView::ResultList &resul
|
||||
|
||||
}
|
||||
|
||||
QStandardItem *InternetSearchModel::BuildContainers(const Song &s, QStandardItem *parent, ContainerKey *key, int level) {
|
||||
QStandardItem *InternetSearchModel::BuildContainers(const Song &s, QStandardItem *parent, ContainerKey *key, const int level) {
|
||||
|
||||
if (level >= 3) {
|
||||
return parent;
|
||||
@@ -249,7 +246,6 @@ QStandardItem *InternetSearchModel::BuildContainers(const Song &s, QStandardItem
|
||||
QStandardItem *container = containers_[*key];
|
||||
if (!container) {
|
||||
container = new QStandardItem(display_text);
|
||||
container->setData(key->provider_index_, Role_ProviderIndex);
|
||||
container->setData(sort_text, CollectionModel::Role_SortText);
|
||||
container->setData(group_by_[level], CollectionModel::Role_ContainerType);
|
||||
|
||||
@@ -275,8 +271,10 @@ QStandardItem *InternetSearchModel::BuildContainers(const Song &s, QStandardItem
|
||||
}
|
||||
|
||||
void InternetSearchModel::Clear() {
|
||||
|
||||
containers_.clear();
|
||||
clear();
|
||||
|
||||
}
|
||||
|
||||
InternetSearchView::ResultList InternetSearchModel::GetChildResults(const QModelIndexList &indexes) const {
|
||||
@@ -314,7 +312,7 @@ void InternetSearchModel::GetChildResults(const QStandardItem *item, InternetSea
|
||||
const QModelIndex parent_proxy_index = proxy_->mapFromSource(item->index());
|
||||
|
||||
// Yes - visit all the children, but do so through the proxy so we get them in the right order.
|
||||
for (int i = 0; i < item->rowCount(); ++i) {
|
||||
for (int i = 0 ; i < item->rowCount() ; ++i) {
|
||||
const QModelIndex proxy_index = parent_proxy_index.model()->index(i, 0, parent_proxy_index);
|
||||
const QModelIndex index = proxy_->mapToSource(proxy_index);
|
||||
GetChildResults(itemFromIndex(index), results, visited);
|
||||
@@ -326,21 +324,6 @@ void InternetSearchModel::GetChildResults(const QStandardItem *item, InternetSea
|
||||
if (result.isValid()) {
|
||||
results->append(result.value<InternetSearchView::Result>());
|
||||
}
|
||||
else {
|
||||
// Maybe it's a provider then?
|
||||
bool is_provider;
|
||||
const int sort_index = item->data(Role_ProviderIndex).toInt(&is_provider);
|
||||
if (is_provider) {
|
||||
// Go through all the items (through the proxy to keep them ordered) and add the ones belonging to this provider to our list
|
||||
for (int i = 0; i < proxy_->rowCount(invisibleRootItem()->index()); ++i) {
|
||||
QModelIndex child_index = proxy_->index(i, 0, invisibleRootItem()->index());
|
||||
const QStandardItem *child_item = itemFromIndex(proxy_->mapToSource(child_index));
|
||||
if (child_item->data(Role_ProviderIndex).toInt() == sort_index) {
|
||||
GetChildResults(child_item, results, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -360,13 +343,13 @@ void GatherResults(const QStandardItem *parent, InternetSearchView::ResultList *
|
||||
(*results).append(result);
|
||||
}
|
||||
|
||||
for (int i = 0; i < parent->rowCount(); ++i) {
|
||||
for (int i = 0 ; i < parent->rowCount() ; ++i) {
|
||||
GatherResults(parent->child(i), results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InternetSearchModel::SetGroupBy(const CollectionModel::Grouping &grouping, bool regroup_now) {
|
||||
void InternetSearchModel::SetGroupBy(const CollectionModel::Grouping &grouping, const bool regroup_now) {
|
||||
|
||||
const CollectionModel::Grouping old_group_by = group_by_;
|
||||
group_by_ = grouping;
|
||||
@@ -389,24 +372,17 @@ MimeData *InternetSearchModel::LoadTracks(const InternetSearchView::ResultList &
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InternetSearchView::ResultList results_copy;
|
||||
for (const InternetSearchView::Result &result : results) {
|
||||
results_copy << result;
|
||||
}
|
||||
|
||||
SongList songs;
|
||||
QList<QUrl> urls;
|
||||
for (const InternetSearchView::Result &result : results) {
|
||||
songs << result.metadata_;
|
||||
urls << result.metadata_.url();
|
||||
}
|
||||
|
||||
InternetSongMimeData *internet_song_mime_data = new InternetSongMimeData(service_);
|
||||
internet_song_mime_data->songs = songs;
|
||||
MimeData *mime_data = internet_song_mime_data;
|
||||
|
||||
QList<QUrl> urls;
|
||||
for (const InternetSearchView::Result &result : results) {
|
||||
urls << result.metadata_.url();
|
||||
}
|
||||
mime_data->setUrls(urls);
|
||||
|
||||
return mime_data;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Strawberry Music Player
|
||||
* This code was part of Clementine (GlobalSearch)
|
||||
* Copyright 2012, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2018, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -54,18 +55,16 @@ class InternetSearchModel : public QStandardItemModel {
|
||||
enum Role {
|
||||
Role_Result = CollectionModel::LastRole,
|
||||
Role_LazyLoadingArt,
|
||||
Role_ProviderIndex,
|
||||
LastRole
|
||||
};
|
||||
|
||||
struct ContainerKey {
|
||||
int provider_index_;
|
||||
QString group_[3];
|
||||
};
|
||||
|
||||
void set_proxy(QSortFilterProxyModel *proxy) { proxy_ = proxy; }
|
||||
void set_use_pretty_covers(const bool pretty) { use_pretty_covers_ = pretty; }
|
||||
void SetGroupBy(const CollectionModel::Grouping &grouping, bool regroup_now);
|
||||
void SetGroupBy(const CollectionModel::Grouping &grouping, const bool regroup_now);
|
||||
|
||||
void Clear();
|
||||
|
||||
@@ -82,7 +81,7 @@ class InternetSearchModel : public QStandardItemModel {
|
||||
void AddResults(const InternetSearchView::ResultList &results);
|
||||
|
||||
private:
|
||||
QStandardItem *BuildContainers(const Song &metadata, QStandardItem *parent, ContainerKey *key, int level = 0);
|
||||
QStandardItem *BuildContainers(const Song &s, QStandardItem *parent, ContainerKey *key, const int level = 0);
|
||||
void GetChildResults(const QStandardItem *item, InternetSearchView::ResultList *results, QSet<const QStandardItem*> *visited) const;
|
||||
|
||||
private:
|
||||
@@ -98,7 +97,7 @@ class InternetSearchModel : public QStandardItemModel {
|
||||
};
|
||||
|
||||
inline uint qHash(const InternetSearchModel::ContainerKey &key) {
|
||||
return qHash(key.provider_index_) ^ qHash(key.group_[0]) ^ qHash(key.group_[1]) ^ qHash(key.group_[2]);
|
||||
return qHash(key.group_[0]) ^ qHash(key.group_[1]) ^ qHash(key.group_[2]);
|
||||
}
|
||||
|
||||
inline bool operator<(const InternetSearchModel::ContainerKey &left, const InternetSearchModel::ContainerKey &right) {
|
||||
@@ -106,7 +105,6 @@ inline bool operator<(const InternetSearchModel::ContainerKey &left, const Inter
|
||||
if (left.field < right.field) return true; \
|
||||
if (left.field > right.field) return false
|
||||
|
||||
CMP(provider_index_);
|
||||
CMP(group_[0]);
|
||||
CMP(group_[1]);
|
||||
CMP(group_[2]);
|
||||
|
||||
@@ -32,15 +32,9 @@
|
||||
#include "internetsearchsortmodel.h"
|
||||
#include "internetsearchview.h"
|
||||
|
||||
InternetSearchSortModel::InternetSearchSortModel(QObject *parent)
|
||||
: QSortFilterProxyModel(parent) {}
|
||||
InternetSearchSortModel::InternetSearchSortModel(QObject *parent) : QSortFilterProxyModel(parent) {}
|
||||
|
||||
bool InternetSearchSortModel::lessThan(const QModelIndex &left, const QModelIndex &right) const {
|
||||
// Compare the provider sort index first.
|
||||
const int index_left = left.data(InternetSearchModel::Role_ProviderIndex).toInt();
|
||||
const int index_right = right.data(InternetSearchModel::Role_ProviderIndex).toInt();
|
||||
if (index_left < index_right) return true;
|
||||
if (index_left > index_right) return false;
|
||||
|
||||
// Dividers always go first
|
||||
if (left.data(CollectionModel::Role_IsDivider).toBool()) return true;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include <QtGlobal>
|
||||
@@ -32,6 +33,7 @@
|
||||
#include <QApplication>
|
||||
#include <QWidget>
|
||||
#include <QTimer>
|
||||
#include <QPair>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
@@ -69,10 +71,13 @@
|
||||
#include "core/mimedata.h"
|
||||
#include "core/iconloader.h"
|
||||
#include "core/song.h"
|
||||
#include "core/logging.h"
|
||||
#include "collection/collectionfilterwidget.h"
|
||||
#include "collection/collectionmodel.h"
|
||||
#include "collection/groupbydialog.h"
|
||||
#include "covermanager/albumcoverloader.h"
|
||||
#include "covermanager/albumcoverloaderoptions.h"
|
||||
#include "covermanager/albumcoverloaderresult.h"
|
||||
#include "internetsongmimedata.h"
|
||||
#include "internetservice.h"
|
||||
#include "internetsearchitemdelegate.h"
|
||||
@@ -102,11 +107,11 @@ InternetSearchView::InternetSearchView(QWidget *parent)
|
||||
back_proxy_(new InternetSearchSortModel(this)),
|
||||
current_proxy_(front_proxy_),
|
||||
swap_models_timer_(new QTimer(this)),
|
||||
use_pretty_covers_(true),
|
||||
search_type_(InternetSearchView::SearchType_Artists),
|
||||
search_error_(false),
|
||||
last_search_id_(0),
|
||||
searches_next_id_(1),
|
||||
art_searches_next_id_(1) {
|
||||
searches_next_id_(1) {
|
||||
|
||||
ui_->setupUi(this);
|
||||
|
||||
@@ -164,7 +169,7 @@ void InternetSearchView::Init(Application *app, InternetService *service) {
|
||||
|
||||
current_model_ = front_model_;
|
||||
current_proxy_ = front_proxy_;
|
||||
|
||||
|
||||
// Set up the sorting proxy model
|
||||
front_proxy_->setSourceModel(front_model_);
|
||||
front_proxy_->setDynamicSortFilter(true);
|
||||
@@ -190,6 +195,7 @@ void InternetSearchView::Init(Application *app, InternetService *service) {
|
||||
connect(ui_->radiobutton_search_albums, SIGNAL(clicked(bool)), SLOT(SearchAlbumsClicked(bool)));
|
||||
connect(ui_->radiobutton_search_songs, SIGNAL(clicked(bool)), SLOT(SearchSongsClicked(bool)));
|
||||
connect(group_by_actions_, SIGNAL(triggered(QAction*)), SLOT(GroupByClicked(QAction*)));
|
||||
connect(group_by_actions_, SIGNAL(triggered(QAction*)), SLOT(GroupByClicked(QAction*)));
|
||||
|
||||
connect(ui_->search, SIGNAL(textChanged(QString)), SLOT(TextEdited(QString)));
|
||||
connect(ui_->results, SIGNAL(AddToPlaylistSignal(QMimeData*)), SIGNAL(AddToPlaylist(QMimeData*)));
|
||||
@@ -201,7 +207,7 @@ void InternetSearchView::Init(Application *app, InternetService *service) {
|
||||
connect(service_, SIGNAL(SearchResults(int, SongList, QString)), SLOT(SearchDone(int, SongList, QString)));
|
||||
|
||||
connect(app_, SIGNAL(SettingsChanged()), SLOT(ReloadSettings()));
|
||||
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QUrl, QImage)), SLOT(AlbumCoverLoaded(quint64, QUrl, QImage)));
|
||||
connect(app_->album_cover_loader(), SIGNAL(AlbumCoverLoaded(quint64, AlbumCoverLoaderResult)), SLOT(AlbumCoverLoaded(quint64, AlbumCoverLoaderResult)));
|
||||
|
||||
ReloadSettings();
|
||||
|
||||
@@ -214,9 +220,9 @@ void InternetSearchView::ReloadSettings() {
|
||||
// Collection settings
|
||||
|
||||
s.beginGroup(service_->settings_group());
|
||||
const bool pretty = s.value("pretty_covers", true).toBool();
|
||||
front_model_->set_use_pretty_covers(pretty);
|
||||
back_model_->set_use_pretty_covers(pretty);
|
||||
use_pretty_covers_ = s.value("pretty_covers", true).toBool();
|
||||
front_model_->set_use_pretty_covers(use_pretty_covers_);
|
||||
back_model_->set_use_pretty_covers(use_pretty_covers_);
|
||||
|
||||
// Internet search settings
|
||||
|
||||
@@ -248,12 +254,6 @@ void InternetSearchView::showEvent(QShowEvent *e) {
|
||||
|
||||
}
|
||||
|
||||
void InternetSearchView::hideEvent(QHideEvent *e) {
|
||||
|
||||
QWidget::hideEvent(e);
|
||||
|
||||
}
|
||||
|
||||
bool InternetSearchView::eventFilter(QObject *object, QEvent *e) {
|
||||
|
||||
if (object == ui_->search && e->type() == QEvent::KeyRelease) {
|
||||
@@ -374,6 +374,7 @@ void InternetSearchView::TextEdited(const QString &text) {
|
||||
const QString trimmed(text.trimmed());
|
||||
|
||||
search_error_ = false;
|
||||
cover_loader_tasks_.clear();
|
||||
|
||||
// Add results to the back model, switch models after some delay.
|
||||
back_model_->Clear();
|
||||
@@ -387,7 +388,7 @@ void InternetSearchView::TextEdited(const QString &text) {
|
||||
// If text query is empty, don't start a new search
|
||||
if (trimmed.isEmpty()) {
|
||||
last_search_id_ = -1;
|
||||
ui_->label_helptext->setText("Enter search terms above to find music");
|
||||
ui_->label_helptext->setText(tr("Enter search terms above to find music"));
|
||||
ui_->label_status->clear();
|
||||
ui_->progressbar->hide();
|
||||
ui_->progressbar->reset();
|
||||
@@ -401,7 +402,7 @@ void InternetSearchView::TextEdited(const QString &text) {
|
||||
|
||||
void InternetSearchView::SwapModels() {
|
||||
|
||||
art_requests_.clear();
|
||||
cover_loader_tasks_.clear();
|
||||
|
||||
std::swap(front_model_, back_model_);
|
||||
std::swap(front_proxy_, back_proxy_);
|
||||
@@ -488,10 +489,8 @@ void InternetSearchView::SearchDone(const int service_id, const SongList &songs,
|
||||
results << result;
|
||||
}
|
||||
|
||||
if (results.isEmpty()) return;
|
||||
|
||||
// Load cached pixmaps into the results
|
||||
for (InternetSearchView::ResultList::iterator it = results.begin(); it != results.end(); ++it) {
|
||||
for (InternetSearchView::ResultList::iterator it = results.begin() ; it != results.end() ; ++it) {
|
||||
it->pixmap_cache_key_ = PixmapCacheKey(*it);
|
||||
}
|
||||
|
||||
@@ -576,7 +575,7 @@ MimeData *InternetSearchView::SelectedMimeData() {
|
||||
QModelIndexList indexes = ui_->results->selectionModel()->selectedRows();
|
||||
if (indexes.isEmpty()) {
|
||||
// There's nothing selected - take the first thing in the model that isn't a divider.
|
||||
for (int i = 0; i < front_proxy_->rowCount(); ++i) {
|
||||
for (int i = 0 ; i < front_proxy_->rowCount() ; ++i) {
|
||||
QModelIndex index = front_proxy_->index(i, 0);
|
||||
if (!index.data(CollectionModel::Role_IsDivider).toBool()) {
|
||||
indexes << index;
|
||||
@@ -663,7 +662,7 @@ void InternetSearchView::GroupByClicked(QAction *action) {
|
||||
if (action->property("group_by").isNull()) {
|
||||
if (!group_by_dialog_) {
|
||||
group_by_dialog_.reset(new GroupByDialog);
|
||||
connect(group_by_dialog_.data(), SIGNAL(Accepted(CollectionModel::Grouping)), SLOT(SetGroupBy(CollectionModel::Grouping)));
|
||||
connect(group_by_dialog_.get(), SIGNAL(Accepted(CollectionModel::Grouping)), SLOT(SetGroupBy(CollectionModel::Grouping)));
|
||||
}
|
||||
|
||||
group_by_dialog_->show();
|
||||
@@ -679,7 +678,8 @@ void InternetSearchView::SetGroupBy(const CollectionModel::Grouping &g) {
|
||||
// Clear requests: changing "group by" on the models will cause all the items to be removed/added again,
|
||||
// so all the QModelIndex here will become invalid. New requests will be created for those
|
||||
// songs when they will be displayed again anyway (when InternetSearchItemDelegate::paint will call LazyLoadAlbumCover)
|
||||
art_requests_.clear();
|
||||
cover_loader_tasks_.clear();
|
||||
|
||||
// Update the models
|
||||
front_model_->SetGroupBy(g, true);
|
||||
back_model_->SetGroupBy(g, false);
|
||||
@@ -722,10 +722,12 @@ void InternetSearchView::SearchSongsClicked(const bool) {
|
||||
void InternetSearchView::SetSearchType(const InternetSearchView::SearchType type) {
|
||||
|
||||
search_type_ = type;
|
||||
|
||||
QSettings s;
|
||||
s.beginGroup(service_->settings_group());
|
||||
s.setValue("type", int(search_type_));
|
||||
s.endGroup();
|
||||
|
||||
TextEdited(ui_->search->text());
|
||||
|
||||
}
|
||||
@@ -761,110 +763,101 @@ void InternetSearchView::AddSongs() {
|
||||
}
|
||||
|
||||
QString InternetSearchView::PixmapCacheKey(const InternetSearchView::Result &result) const {
|
||||
return "internet:" % result.metadata_.url().toString();
|
||||
|
||||
if (result.metadata_.art_automatic_is_valid()) {
|
||||
return Song::TextForSource(service_->source()) + "/" + result.metadata_.art_automatic().toString();
|
||||
}
|
||||
else if (!result.metadata_.effective_albumartist().isEmpty() && !result.metadata_.album().isEmpty()) {
|
||||
return Song::TextForSource(service_->source()) + "/" + result.metadata_.effective_albumartist() + "/" + result.metadata_.album();
|
||||
}
|
||||
else {
|
||||
return Song::TextForSource(service_->source()) + "/" + result.metadata_.url().toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool InternetSearchView::FindCachedPixmap(const InternetSearchView::Result &result, QPixmap *pixmap) const {
|
||||
return pixmap_cache_.find(result.pixmap_cache_key_, pixmap);
|
||||
}
|
||||
|
||||
int InternetSearchView::LoadAlbumCoverAsync(const InternetSearchView::Result &result) {
|
||||
|
||||
const int id = art_searches_next_id_++;
|
||||
|
||||
pending_art_searches_[id] = result.pixmap_cache_key_;
|
||||
|
||||
quint64 loader_id = app_->album_cover_loader()->LoadImageAsync(cover_loader_options_, result.metadata_);
|
||||
cover_loader_tasks_[loader_id] = id;
|
||||
|
||||
return id;
|
||||
|
||||
}
|
||||
|
||||
void InternetSearchView::LazyLoadAlbumCover(const QModelIndex &proxy_index) {
|
||||
|
||||
if (!proxy_index.isValid() || proxy_index.model() != front_proxy_) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QModelIndex source_index = front_proxy_->mapToSource(proxy_index);
|
||||
if (!source_index.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Already loading art for this item?
|
||||
if (proxy_index.data(InternetSearchModel::Role_LazyLoadingArt).isValid()) {
|
||||
if (source_index.data(InternetSearchModel::Role_LazyLoadingArt).isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Should we even load art at all?
|
||||
if (!app_->collection_model()->use_pretty_covers()) {
|
||||
if (!use_pretty_covers_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this an album?
|
||||
const CollectionModel::GroupBy container_type = CollectionModel::GroupBy(proxy_index.data(CollectionModel::Role_ContainerType).toInt());
|
||||
if (container_type != CollectionModel::GroupBy_Album &&
|
||||
container_type != CollectionModel::GroupBy_AlbumDisc &&
|
||||
container_type != CollectionModel::GroupBy_YearAlbum &&
|
||||
container_type != CollectionModel::GroupBy_YearAlbumDisc &&
|
||||
container_type != CollectionModel::GroupBy_OriginalYearAlbum) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark the item as loading art
|
||||
const QModelIndex source_index = front_proxy_->mapToSource(proxy_index);
|
||||
QStandardItem *item = front_model_->itemFromIndex(source_index);
|
||||
item->setData(true, InternetSearchModel::Role_LazyLoadingArt);
|
||||
|
||||
QStandardItem *item_album = front_model_->itemFromIndex(source_index);
|
||||
if (!item_album) {
|
||||
return;
|
||||
}
|
||||
item_album->setData(true, InternetSearchModel::Role_LazyLoadingArt);
|
||||
|
||||
// Walk down the item's children until we find a track
|
||||
while (item->rowCount()) {
|
||||
item = item->child(0);
|
||||
QStandardItem *item_song = item_album;
|
||||
while (item_song->rowCount()) {
|
||||
item_song = item_song->child(0);
|
||||
}
|
||||
|
||||
// Get the track's Result
|
||||
const InternetSearchView::Result result = item->data(InternetSearchModel::Role_Result).value<InternetSearchView::Result>();
|
||||
const InternetSearchView::Result result = item_song->data(InternetSearchModel::Role_Result).value<InternetSearchView::Result>();
|
||||
|
||||
// Load the art.
|
||||
int id = LoadAlbumCoverAsync(result);
|
||||
art_requests_[id] = source_index;
|
||||
|
||||
}
|
||||
|
||||
void InternetSearchView::AlbumCoverLoaded(const quint64 id, const QUrl&, const QImage &image) {
|
||||
|
||||
if (!cover_loader_tasks_.contains(id)) return;
|
||||
int orig_id = cover_loader_tasks_.take(id);
|
||||
|
||||
const QString key = pending_art_searches_.take(orig_id);
|
||||
|
||||
QPixmap pixmap = QPixmap::fromImage(image);
|
||||
pixmap_cache_.insert(key, pixmap);
|
||||
|
||||
if (!art_requests_.contains(id)) return;
|
||||
QModelIndex idx = art_requests_.take(id);
|
||||
|
||||
if (!pixmap.isNull() && idx.isValid()) {
|
||||
front_model_->itemFromIndex(idx)->setData(pixmap, Qt::DecorationRole);
|
||||
QPixmap cached_pixmap;
|
||||
if (pixmap_cache_.find(result.pixmap_cache_key_, &cached_pixmap)) {
|
||||
item_album->setData(cached_pixmap, Qt::DecorationRole);
|
||||
}
|
||||
else {
|
||||
quint64 loader_id = app_->album_cover_loader()->LoadImageAsync(cover_loader_options_, result.metadata_);
|
||||
cover_loader_tasks_[loader_id] = qMakePair(source_index, result.pixmap_cache_key_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QImage InternetSearchView::ScaleAndPad(const QImage &image) {
|
||||
void InternetSearchView::AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &albumcover_result) {
|
||||
|
||||
if (image.isNull()) return QImage();
|
||||
if (!cover_loader_tasks_.contains(id)) {
|
||||
return;
|
||||
}
|
||||
QPair<QModelIndex, QString> cover_loader_task = cover_loader_tasks_.take(id);
|
||||
QModelIndex idx = cover_loader_task.first;
|
||||
QString key = cover_loader_task.second;
|
||||
|
||||
const QSize target_size = QSize(kArtHeight, kArtHeight);
|
||||
QPixmap pixmap = QPixmap::fromImage(albumcover_result.image_scaled);
|
||||
if (!pixmap.isNull()) {
|
||||
pixmap_cache_.insert(key, pixmap);
|
||||
}
|
||||
|
||||
if (image.size() == target_size) return image;
|
||||
|
||||
// Scale the image down
|
||||
QImage copy;
|
||||
copy = image.scaled(target_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
// Pad the image to kHeight x kHeight
|
||||
if (copy.size() == target_size) return copy;
|
||||
|
||||
QImage padded_image(kArtHeight, kArtHeight, QImage::Format_ARGB32);
|
||||
padded_image.fill(0);
|
||||
|
||||
QPainter p(&padded_image);
|
||||
p.drawImage((kArtHeight - copy.width()) / 2, (kArtHeight - copy.height()) / 2, copy);
|
||||
p.end();
|
||||
|
||||
return padded_image;
|
||||
if (idx.isValid()) {
|
||||
QStandardItem *item = front_model_->itemFromIndex(idx);
|
||||
if (item) {
|
||||
item->setData(albumcover_result.image_scaled, Qt::DecorationRole);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,10 +24,13 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
#include <QWidget>
|
||||
#include <QSet>
|
||||
#include <QPair>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
@@ -36,12 +39,12 @@
|
||||
#include <QImage>
|
||||
#include <QPixmap>
|
||||
#include <QPixmapCache>
|
||||
#include <QScopedPointer>
|
||||
#include <QMetaType>
|
||||
|
||||
#include "core/song.h"
|
||||
#include "collection/collectionmodel.h"
|
||||
#include "covermanager/albumcoverloaderoptions.h"
|
||||
#include "covermanager/albumcoverloaderresult.h"
|
||||
#include "settings/settingsdialog.h"
|
||||
|
||||
class QSortFilterProxyModel;
|
||||
@@ -53,14 +56,12 @@ class QActionGroup;
|
||||
class QEvent;
|
||||
class QKeyEvent;
|
||||
class QShowEvent;
|
||||
class QHideEvent;
|
||||
class QContextMenuEvent;
|
||||
class QTimerEvent;
|
||||
|
||||
class Application;
|
||||
class MimeData;
|
||||
class GroupByDialog;
|
||||
class AlbumCoverLoader;
|
||||
class InternetService;
|
||||
class InternetSearchModel;
|
||||
class Ui_InternetSearchView;
|
||||
@@ -104,7 +105,6 @@ class InternetSearchView : public QWidget {
|
||||
};
|
||||
|
||||
void showEvent(QShowEvent *e);
|
||||
void hideEvent(QHideEvent *e);
|
||||
bool eventFilter(QObject *object, QEvent *e);
|
||||
void timerEvent(QTimerEvent *e);
|
||||
|
||||
@@ -135,7 +135,6 @@ class InternetSearchView : public QWidget {
|
||||
|
||||
QString PixmapCacheKey(const Result &result) const;
|
||||
bool FindCachedPixmap(const Result &result, QPixmap *pixmap) const;
|
||||
static QImage ScaleAndPad(const QImage &image);
|
||||
int LoadAlbumCoverAsync(const Result &result);
|
||||
|
||||
signals:
|
||||
@@ -173,7 +172,7 @@ class InternetSearchView : public QWidget {
|
||||
void GroupByClicked(QAction *action);
|
||||
void SetGroupBy(const CollectionModel::Grouping &g);
|
||||
|
||||
void AlbumCoverLoaded(const quint64 id, const QUrl&, const QImage &image);
|
||||
void AlbumCoverLoaded(const quint64 id, const AlbumCoverLoaderResult &albumcover_result);
|
||||
|
||||
public slots:
|
||||
void ReloadSettings();
|
||||
@@ -187,7 +186,7 @@ class InternetSearchView : public QWidget {
|
||||
Application *app_;
|
||||
InternetService *service_;
|
||||
Ui_InternetSearchView *ui_;
|
||||
QScopedPointer<GroupByDialog> group_by_dialog_;
|
||||
std::unique_ptr<GroupByDialog> group_by_dialog_;
|
||||
|
||||
QMenu *context_menu_;
|
||||
QList<QAction*> context_actions_;
|
||||
@@ -206,18 +205,17 @@ class InternetSearchView : public QWidget {
|
||||
|
||||
QTimer *swap_models_timer_;
|
||||
|
||||
bool use_pretty_covers_;
|
||||
SearchType search_type_;
|
||||
bool search_error_;
|
||||
int last_search_id_;
|
||||
int searches_next_id_;
|
||||
int art_searches_next_id_;
|
||||
|
||||
QMap<int, DelayedSearch> delayed_searches_;
|
||||
QMap<int, PendingState> pending_searches_;
|
||||
QMap<int, QString> pending_art_searches_;
|
||||
QMap<int, QModelIndex> art_requests_;
|
||||
|
||||
AlbumCoverLoaderOptions cover_loader_options_;
|
||||
QMap<quint64, quint64> cover_loader_tasks_;
|
||||
QMap<quint64, QPair<QModelIndex, QString>> cover_loader_tasks_;
|
||||
QPixmapCache pixmap_cache_;
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user