Add a spotify global search provider

This commit is contained in:
David Sansome 2011-08-28 23:59:18 +01:00
parent dbe8ffd5de
commit 8dea8a2664
8 changed files with 228 additions and 27 deletions

View File

@ -671,6 +671,7 @@ endif(HAVE_LIBLASTFM)
if(HAVE_SPOTIFY)
list(APPEND SOURCES
globalsearch/spotifysearchprovider.cpp
internet/spotifyblobdownloader.cpp
internet/spotifysearchplaylisttype.cpp
internet/spotifyserver.cpp
@ -680,6 +681,7 @@ if(HAVE_SPOTIFY)
resolvers/spotifyresolver.cpp
)
list(APPEND HEADERS
globalsearch/spotifysearchprovider.h
internet/spotifyblobdownloader.h
internet/spotifyserver.h
internet/spotifyservice.h

View File

@ -31,31 +31,7 @@ GlobalSearchItemDelegate::GlobalSearchItemDelegate(GlobalSearchWidget* widget)
: QStyledItemDelegate(widget),
widget_(widget)
{
no_cover_ = ScaleAndPad(QImage(":nocover.png"));
}
QPixmap GlobalSearchItemDelegate::ScaleAndPad(const QImage& image) {
if (image.isNull())
return QPixmap();
if (image.size() == QSize(kHeight, kHeight))
return QPixmap::fromImage(image);
// Scale the image down
QImage copy;
copy = image.scaled(QSize(kHeight, kHeight),
Qt::KeepAspectRatio, Qt::SmoothTransformation);
// Pad the image to kHeight x kHeight
QImage padded_image(kHeight, kHeight, QImage::Format_ARGB32);
padded_image.fill(0);
QPainter p(&padded_image);
p.drawImage((kHeight - copy.width()) / 2, (kHeight - copy.height()) / 2,
copy);
p.end();
return QPixmap::fromImage(padded_image);
no_cover_ = QPixmap::fromImage(QImage(":nocover.png"));
}
QSize GlobalSearchItemDelegate::sizeHint(const QStyleOptionViewItem& option,

View File

@ -32,8 +32,6 @@ public:
static const int kArtMargin;
static const int kWordPadding;
static QPixmap ScaleAndPad(const QImage& image);
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const;
void paint(QPainter* painter, const QStyleOptionViewItem& option,
const QModelIndex& index) const;

View File

@ -15,6 +15,7 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "globalsearch.h"
#include "globalsearchitemdelegate.h"
#include "globalsearchsortmodel.h"
@ -25,6 +26,10 @@
#include "core/utilities.h"
#include "widgets/stylehelper.h"
#ifdef HAVE_SPOTIFY
# include "spotifysearchprovider.h"
#endif
#include <QListView>
#include <QPainter>
#include <QSortFilterProxyModel>
@ -75,9 +80,14 @@ GlobalSearchWidget::~GlobalSearchWidget() {
}
void GlobalSearchWidget::Init(LibraryBackendInterface* library) {
// Add providers
engine_->AddProvider(new LibrarySearchProvider(
library, tr("Library"), IconLoader::Load("folder-sound"), engine_));
#ifdef HAVE_SPOTIFY
engine_->AddProvider(new SpotifySearchProvider(engine_));
#endif
// The style helper's base color doesn't get initialised until after the
// constructor.
QPalette view_palette = view_->palette();

View File

@ -18,6 +18,7 @@
#include "searchprovider.h"
#include "core/boundfuturewatcher.h"
#include <QPainter>
#include <QtConcurrentRun>
const int SearchProvider::kArtHeight = 32;
@ -87,3 +88,31 @@ void BlockingSearchProvider::BlockingSearchFinished() {
emit ResultsAvailable(id, watcher->result());
emit SearchFinished(id);
}
QImage SearchProvider::ScaleAndPad(const QImage& image) {
if (image.isNull())
return QImage();
const QSize target_size = QSize(kArtHeight, kArtHeight);
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;
}

View File

@ -83,6 +83,8 @@ public:
// ResultsAvailable. Must emit TracksLoaded exactly once with this ID.
virtual void LoadTracksAsync(int id, const Result& result) = 0;
static QImage ScaleAndPad(const QImage& image);
signals:
void ResultsAvailable(int id, const SearchProvider::ResultList& results);
void SearchFinished(int id);

View File

@ -0,0 +1,125 @@
/* 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 "spotifysearchprovider.h"
#include "internet/internetmodel.h"
#include "internet/spotifyserver.h"
#include "internet/spotifyservice.h"
SpotifySearchProvider::SpotifySearchProvider(QObject* parent)
: SearchProvider("Spotify", QIcon(":icons/svg/spotify.svg"), parent),
server_(NULL),
service_(NULL)
{
}
SpotifyServer* SpotifySearchProvider::server() {
if (server_)
return server_;
if (!service_)
service_ = InternetModel::Service<SpotifyService>();
if (service_->login_state() != SpotifyService::LoginState_LoggedIn)
return NULL;
server_ = service_->server();
connect(server_, SIGNAL(SearchResults(protobuf::SearchResponse)),
SLOT(SearchFinishedSlot(protobuf::SearchResponse)));
connect(server_, SIGNAL(ImageLoaded(QString,QImage)),
SLOT(ArtLoadedSlot(QString,QImage)));
connect(server_, SIGNAL(destroyed()), SLOT(ServerDestroyed()));
return service_->server();
}
void SpotifySearchProvider::ServerDestroyed() {
server_ = NULL;
}
void SpotifySearchProvider::SearchAsync(int id, const QString& query) {
SpotifyServer* s = server();
if (!s) {
emit SearchFinished(id);
return;
}
PendingState state;
state.orig_id_ = id;
state.tokens_ = TokenizeQuery(query);
const QString query_string = state.tokens_.join(" ");
s->Search(query_string, 25);
queries_[query_string] = state;
}
void SpotifySearchProvider::SearchFinishedSlot(const protobuf::SearchResponse& response) {
QString query_string = QString::fromUtf8(response.request().query().c_str());
QMap<QString, PendingState>::iterator it = queries_.find(query_string);
if (it == queries_.end())
return;
PendingState state = it.value();
queries_.erase(it);
ResultList ret;
for (int i = 0; i < response.result_size(); ++i) {
const protobuf::Track& track = response.result(i);
Result result(this);
result.type_ = Result::Type_Track;
SpotifyService::SongFromProtobuf(track, &result.metadata_);
result.match_quality_ = MatchQuality(state.tokens_, result.metadata_.title());
ret << result;
}
emit ResultsAvailable(state.orig_id_, ret);
emit SearchFinished(state.orig_id_);
}
void SpotifySearchProvider::LoadArtAsync(int id, const Result& result) {
SpotifyServer* s = server();
if (!s) {
emit ArtLoaded(id, QImage());
return;
}
QString image_id = result.metadata_.url().path();
if (image_id.startsWith('/'))
image_id.remove(0, 1);
pending_art_[image_id] = id;
s->LoadImage(image_id);
}
void SpotifySearchProvider::ArtLoadedSlot(const QString& id, const QImage& image) {
QMap<QString, int>::iterator it = pending_art_.find(id);
if (it == pending_art_.end())
return;
const int orig_id = it.value();
pending_art_.erase(it);
emit ArtLoaded(orig_id, ScaleAndPad(image));
}
void SpotifySearchProvider::LoadTracksAsync(int id, const Result& result) {
emit TracksLoaded(id, SongList());
}

View File

@ -0,0 +1,59 @@
/* 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 SPOTIFYSEARCHPROVIDER_H
#define SPOTIFYSEARCHPROVIDER_H
#include "searchprovider.h"
#include "spotifyblob/common/spotifymessages.pb.h"
class SpotifyServer;
class SpotifyService;
class SpotifySearchProvider : public SearchProvider {
Q_OBJECT
public:
SpotifySearchProvider(QObject* parent = 0);
void SearchAsync(int id, const QString& query);
void LoadArtAsync(int id, const Result& result);
void LoadTracksAsync(int id, const Result& result);
private slots:
void ServerDestroyed();
void SearchFinishedSlot(const protobuf::SearchResponse& response);
void ArtLoadedSlot(const QString& id, const QImage& image);
private:
struct PendingState {
int orig_id_;
QStringList tokens_;
};
SpotifyServer* server();
private:
SpotifyServer* server_;
SpotifyService* service_;
QMap<QString, PendingState> queries_;
QMap<QString, int> pending_art_;
};
#endif // SPOTIFYSEARCHPROVIDER_H