diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c01a10559..33aa39a2c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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
diff --git a/src/globalsearch/globalsearchitemdelegate.cpp b/src/globalsearch/globalsearchitemdelegate.cpp
index 467d8093b..d4b03d84a 100644
--- a/src/globalsearch/globalsearchitemdelegate.cpp
+++ b/src/globalsearch/globalsearchitemdelegate.cpp
@@ -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,
diff --git a/src/globalsearch/globalsearchitemdelegate.h b/src/globalsearch/globalsearchitemdelegate.h
index a07f5704f..49300d862 100644
--- a/src/globalsearch/globalsearchitemdelegate.h
+++ b/src/globalsearch/globalsearchitemdelegate.h
@@ -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;
diff --git a/src/globalsearch/globalsearchwidget.cpp b/src/globalsearch/globalsearchwidget.cpp
index b438a2ff2..a9b38c5c4 100644
--- a/src/globalsearch/globalsearchwidget.cpp
+++ b/src/globalsearch/globalsearchwidget.cpp
@@ -15,6 +15,7 @@
along with Clementine. If not, see .
*/
+#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
#include
#include
@@ -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();
diff --git a/src/globalsearch/searchprovider.cpp b/src/globalsearch/searchprovider.cpp
index 4f9776b9e..fd8e8e3a7 100644
--- a/src/globalsearch/searchprovider.cpp
+++ b/src/globalsearch/searchprovider.cpp
@@ -18,6 +18,7 @@
#include "searchprovider.h"
#include "core/boundfuturewatcher.h"
+#include
#include
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;
+}
diff --git a/src/globalsearch/searchprovider.h b/src/globalsearch/searchprovider.h
index 48fa39603..2d38cd63d 100644
--- a/src/globalsearch/searchprovider.h
+++ b/src/globalsearch/searchprovider.h
@@ -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);
diff --git a/src/globalsearch/spotifysearchprovider.cpp b/src/globalsearch/spotifysearchprovider.cpp
new file mode 100644
index 000000000..a33733e12
--- /dev/null
+++ b/src/globalsearch/spotifysearchprovider.cpp
@@ -0,0 +1,125 @@
+/* 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 "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();
+
+ 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::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::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());
+}
+
+
diff --git a/src/globalsearch/spotifysearchprovider.h b/src/globalsearch/spotifysearchprovider.h
new file mode 100644
index 000000000..676a7a646
--- /dev/null
+++ b/src/globalsearch/spotifysearchprovider.h
@@ -0,0 +1,59 @@
+/* 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 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 queries_;
+ QMap pending_art_;
+};
+
+#endif // SPOTIFYSEARCHPROVIDER_H