Add a spotify global search provider
This commit is contained in:
parent
dbe8ffd5de
commit
8dea8a2664
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
125
src/globalsearch/spotifysearchprovider.cpp
Normal file
125
src/globalsearch/spotifysearchprovider.cpp
Normal 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());
|
||||
}
|
||||
|
||||
|
59
src/globalsearch/spotifysearchprovider.h
Normal file
59
src/globalsearch/spotifysearchprovider.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user