Allow the artist and album to be specified separately when searching for album covers

This commit is contained in:
David Sansome 2011-06-22 19:07:15 +00:00
parent 2e9ec3e9d4
commit bac414a630
14 changed files with 67 additions and 45 deletions

View File

@ -39,8 +39,9 @@ class AmazonCoverProvider(clementine.CoverProvider):
self.network = clementine.NetworkAccessManager() self.network = clementine.NetworkAccessManager()
def StartSearch(self, query, id): def StartSearch(self, artist, album, id):
url = QUrl.fromEncoded(self.API_URL.format(self.PrepareAmazonRESTUrl(query))) query = self.PrepareAmazonRESTUrl(artist + " " + album)
url = QUrl.fromEncoded(self.API_URL.format(query))
LOGGER.debug("ID %d: Sending request to '%s'" % (id, url)) LOGGER.debug("ID %d: Sending request to '%s'" % (id, url))
reply = self.network.get(QNetworkRequest(url)) reply = self.network.get(QNetworkRequest(url))

View File

@ -25,8 +25,8 @@ class GoogleImagesCoverProvider(clementine.CoverProvider):
} }
self.network = clementine.NetworkAccessManager() self.network = clementine.NetworkAccessManager()
def StartSearch(self, query, id): def StartSearch(self, artist, album, id):
url = self.GetQueryURL(query) url = self.GetQueryURL(artist + " " + album)
LOGGER.info("Id %d - sending request to '%s'" % (id, url)) LOGGER.info("Id %d - sending request to '%s'" % (id, url))
reply = self.network.get(QNetworkRequest(url)) reply = self.network.get(QNetworkRequest(url))
@ -34,12 +34,12 @@ class GoogleImagesCoverProvider(clementine.CoverProvider):
def QueryFinished(): def QueryFinished():
LOGGER.debug("Id %d - finished" % id) LOGGER.debug("Id %d - finished" % id)
self.SearchFinished(id, self.ParseReply(query, reply)) self.SearchFinished(id, self.ParseReply(artist, album, reply))
reply.connect("finished()", QueryFinished) reply.connect("finished()", QueryFinished)
return True return True
def ParseReply(self, query, reply): def ParseReply(self, artist, album, reply):
results = json.loads(str(reply.readAll())) results = json.loads(str(reply.readAll()))
parsed = [] parsed = []
@ -48,7 +48,9 @@ class GoogleImagesCoverProvider(clementine.CoverProvider):
LOGGER.warning("Error parsing reply: %s", results["responseDetails"]) LOGGER.warning("Error parsing reply: %s", results["responseDetails"])
return parsed return parsed
LOGGER.info("Parsing reply for query '%s'", query) query = "%s - %s" % (artist, album)
LOGGER.info("Parsing reply for query '%s'" % query)
for result in results['responseData']['results']: for result in results['responseData']['results']:
current = clementine.CoverSearchResult() current = clementine.CoverSearchResult()

View File

@ -33,10 +33,11 @@ AlbumCoverFetcher::AlbumCoverFetcher(QObject* parent, QNetworkAccessManager* net
connect(request_starter_, SIGNAL(timeout()), SLOT(StartRequests())); connect(request_starter_, SIGNAL(timeout()), SLOT(StartRequests()));
} }
quint64 AlbumCoverFetcher::FetchAlbumCover( quint64 AlbumCoverFetcher::FetchAlbumCover(const QString& artist,
const QString& artist_name, const QString& album_name) { const QString& album) {
CoverSearchRequest request; CoverSearchRequest request;
request.query = artist_name + " " + album_name; request.artist = artist;
request.album = album;
request.search = false; request.search = false;
request.id = next_id_ ++; request.id = next_id_ ++;
@ -44,9 +45,11 @@ quint64 AlbumCoverFetcher::FetchAlbumCover(
return request.id; return request.id;
} }
quint64 AlbumCoverFetcher::SearchForCovers(const QString &query) { quint64 AlbumCoverFetcher::SearchForCovers(const QString& artist,
const QString& album) {
CoverSearchRequest request; CoverSearchRequest request;
request.query = query; request.artist = artist;
request.album = album;
request.search = true; request.search = true;
request.id = next_id_ ++; request.id = next_id_ ++;
@ -81,8 +84,8 @@ void AlbumCoverFetcher::StartRequests() {
// search objects are this fetcher's children so worst case scenario - they get // search objects are this fetcher's children so worst case scenario - they get
// deleted with it // deleted with it
AlbumCoverFetcherSearch* search = new AlbumCoverFetcherSearch(request, network_, AlbumCoverFetcherSearch* search = new AlbumCoverFetcherSearch(
this); request, network_, this);
active_requests_.insert(request.id, search); active_requests_.insert(request.id, search);
connect(search, SIGNAL(SearchFinished(quint64, CoverSearchResults)), connect(search, SIGNAL(SearchFinished(quint64, CoverSearchResults)),

View File

@ -38,8 +38,11 @@ class AlbumCoverFetcherSearch;
struct CoverSearchRequest { struct CoverSearchRequest {
// an unique (for one AlbumCoverFetcher) request identifier // an unique (for one AlbumCoverFetcher) request identifier
quint64 id; quint64 id;
// a search query // a search query
QString query; QString artist;
QString album;
// is this only a search request or should we also fetch the first // is this only a search request or should we also fetch the first
// cover that's found? // cover that's found?
bool search; bool search;
@ -77,7 +80,7 @@ class AlbumCoverFetcher : public QObject {
static const int kMaxConcurrentRequests; static const int kMaxConcurrentRequests;
quint64 SearchForCovers(const QString& query); quint64 SearchForCovers(const QString& artist, const QString& album);
quint64 FetchAlbumCover(const QString& artist, const QString& album); quint64 FetchAlbumCover(const QString& artist, const QString& album);
void Clear(); void Clear();

View File

@ -60,18 +60,19 @@ void AlbumCoverFetcherSearch::TerminateSearch() {
void AlbumCoverFetcherSearch::Start() { void AlbumCoverFetcherSearch::Start() {
CoverProviders* providers = &CoverProviders::instance(); CoverProviders* providers = &CoverProviders::instance();
// end this search before it even began if there are no providers...
foreach(CoverProvider* provider, providers->List()) { foreach(CoverProvider* provider, providers->List()) {
connect(provider, SIGNAL(SearchFinished(int,QList<CoverSearchResult>)), connect(provider, SIGNAL(SearchFinished(int,QList<CoverSearchResult>)),
SLOT(ProviderSearchFinished(int,QList<CoverSearchResult>))); SLOT(ProviderSearchFinished(int,QList<CoverSearchResult>)));
const int id = providers->NextId(); const int id = providers->NextId();
const bool success = provider->StartSearch(request_.query, id); const bool success = provider->StartSearch(
request_.artist, request_.album, id);
if (success) { if (success) {
pending_requests_[id] = provider; pending_requests_[id] = provider;
} }
} }
// end this search before it even began if there are no providers...
if(pending_requests_.isEmpty()) { if(pending_requests_.isEmpty()) {
TerminateSearch(); TerminateSearch();
} }

View File

@ -40,7 +40,7 @@ public:
// Starts searching for covers matching the given query text. Returns true // Starts searching for covers matching the given query text. Returns true
// if the query has been started, or false if an error occurred. The provider // if the query has been started, or false if an error occurred. The provider
// should remember the ID and emit it along with the result when it finishes. // should remember the ID and emit it along with the result when it finishes.
virtual bool StartSearch(const QString& query, int id) = 0; virtual bool StartSearch(const QString& artist, const QString& album, int id) = 0;
virtual void CancelSearch(int id) {} virtual void CancelSearch(int id) {}

View File

@ -30,10 +30,10 @@ LastFmCoverProvider::LastFmCoverProvider(QObject* parent)
{ {
} }
bool LastFmCoverProvider::StartSearch(const QString& query, int id) { bool LastFmCoverProvider::StartSearch(const QString& artist, const QString& album, int id) {
QMap<QString, QString> params; QMap<QString, QString> params;
params["method"] = "album.search"; params["method"] = "album.search";
params["album"] = query; params["album"] = album + " " + artist;
QNetworkReply* reply = lastfm::ws::post(params); QNetworkReply* reply = lastfm::ws::post(params);
connect(reply, SIGNAL(finished()), SLOT(QueryFinished())); connect(reply, SIGNAL(finished()), SLOT(QueryFinished()));

View File

@ -33,7 +33,7 @@ class LastFmCoverProvider : public CoverProvider {
public: public:
LastFmCoverProvider(QObject* parent); LastFmCoverProvider(QObject* parent);
bool StartSearch(const QString& query, int id); bool StartSearch(const QString& artist, const QString& album, int id);
private slots: private slots:
void QueryFinished(); void QueryFinished();

View File

@ -79,16 +79,16 @@ if (_wrapper) {
} }
CoverProvider::CancelSearch(id); CoverProvider::CancelSearch(id);
} }
bool PythonQtShell_CoverProvider::StartSearch(const QString& query, int id) bool PythonQtShell_CoverProvider::StartSearch(const QString& artist, const QString& album, int id)
{ {
if (_wrapper) { if (_wrapper) {
PyObject* obj = PyObject_GetAttrString((PyObject*)_wrapper, "StartSearch"); PyObject* obj = PyObject_GetAttrString((PyObject*)_wrapper, "StartSearch");
PyErr_Clear(); PyErr_Clear();
if (obj && !PythonQtSlotFunction_Check(obj)) { if (obj && !PythonQtSlotFunction_Check(obj)) {
static const char* argumentList[] ={"bool" , "const QString&" , "int"}; static const char* argumentList[] ={"bool" , "const QString&" , "const QString&" , "int"};
static const PythonQtMethodInfo* methodInfo = PythonQtMethodInfo::getCachedMethodInfoFromArgumentList(3, argumentList); static const PythonQtMethodInfo* methodInfo = PythonQtMethodInfo::getCachedMethodInfoFromArgumentList(4, argumentList);
bool returnValue = 0; bool returnValue = 0;
void* args[3] = {NULL, (void*)&query, (void*)&id}; void* args[4] = {NULL, (void*)&artist, (void*)&album, (void*)&id};
PyObject* result = PythonQtSignalTarget::call(obj, methodInfo, args, true); PyObject* result = PythonQtSignalTarget::call(obj, methodInfo, args, true);
if (result) { if (result) {
args[0] = PythonQtConv::ConvertPythonToQt(methodInfo->parameters().at(0), result, false, NULL, &returnValue); args[0] = PythonQtConv::ConvertPythonToQt(methodInfo->parameters().at(0), result, false, NULL, &returnValue);

View File

@ -74,7 +74,7 @@ public:
PythonQtShell_CoverProvider(const QString& name, QObject* parent):CoverProvider(name, parent),_wrapper(NULL) {}; PythonQtShell_CoverProvider(const QString& name, QObject* parent):CoverProvider(name, parent),_wrapper(NULL) {};
virtual void CancelSearch(int id); virtual void CancelSearch(int id);
virtual bool StartSearch(const QString& query, int id); virtual bool StartSearch(const QString& artist, const QString& album, int id);
virtual void childEvent(QChildEvent* arg__1); virtual void childEvent(QChildEvent* arg__1);
virtual void customEvent(QEvent* arg__1); virtual void customEvent(QEvent* arg__1);
virtual bool event(QEvent* arg__1); virtual bool event(QEvent* arg__1);

View File

@ -158,12 +158,7 @@ QString AlbumCoverChoiceController::LoadCoverFromURL(Song* song) {
QString AlbumCoverChoiceController::SearchForCover(Song* song) { QString AlbumCoverChoiceController::SearchForCover(Song* song) {
// Get something sensible to stick in the search box // Get something sensible to stick in the search box
QString query = song->artist(); QImage image = cover_searcher_->Exec(song->artist(), song->album());
if (!query.isEmpty())
query += " ";
query += song->album();
QImage image = cover_searcher_->Exec(query);
if(!image.isNull()) { if(!image.isNull()) {
QString cover = SaveCoverInCache(song->artist(), song->album(), image); QString cover = SaveCoverInCache(song->artist(), song->album(), image);

View File

@ -115,11 +115,12 @@ void AlbumCoverSearcher::Init(AlbumCoverFetcher* fetcher) {
connect(fetcher_, SIGNAL(SearchFinished(quint64,CoverSearchResults)), SLOT(SearchFinished(quint64,CoverSearchResults))); connect(fetcher_, SIGNAL(SearchFinished(quint64,CoverSearchResults)), SLOT(SearchFinished(quint64,CoverSearchResults)));
} }
QImage AlbumCoverSearcher::Exec(const QString &query) { QImage AlbumCoverSearcher::Exec(const QString& artist, const QString& album) {
ui_->query->setText(query); ui_->artist->setText(artist);
ui_->query->setFocus(); ui_->album->setText(album);
ui_->artist->setFocus();
if(!query.isEmpty()) { if(!artist.isEmpty() || !album.isEmpty()) {
Search(); Search();
} }
@ -140,13 +141,14 @@ QImage AlbumCoverSearcher::Exec(const QString &query) {
void AlbumCoverSearcher::Search() { void AlbumCoverSearcher::Search() {
ui_->busy->show(); ui_->busy->show();
ui_->search->setEnabled(false); ui_->search->setEnabled(false);
ui_->query->setEnabled(false); ui_->artist->setEnabled(false);
ui_->album->setEnabled(false);
ui_->covers->setEnabled(false); ui_->covers->setEnabled(false);
model_->clear(); model_->clear();
cover_loading_tasks_.clear(); cover_loading_tasks_.clear();
id_ = fetcher_->SearchForCovers(ui_->query->text()); id_ = fetcher_->SearchForCovers(ui_->artist->text(), ui_->album->text());
} }
void AlbumCoverSearcher::SearchFinished(quint64 id, const CoverSearchResults& results) { void AlbumCoverSearcher::SearchFinished(quint64 id, const CoverSearchResults& results) {
@ -154,7 +156,8 @@ void AlbumCoverSearcher::SearchFinished(quint64 id, const CoverSearchResults& re
return; return;
ui_->search->setEnabled(true); ui_->search->setEnabled(true);
ui_->query->setEnabled(true); ui_->artist->setEnabled(true);
ui_->album->setEnabled(true);
ui_->covers->setEnabled(true); ui_->covers->setEnabled(true);
id_ = 0; id_ = 0;

View File

@ -67,7 +67,7 @@ public:
void Init(AlbumCoverFetcher* fetcher); void Init(AlbumCoverFetcher* fetcher);
QImage Exec(const QString& query); QImage Exec(const QString& artist, const QString& album);
protected: protected:
void keyPressEvent(QKeyEvent *); void keyPressEvent(QKeyEvent *);

View File

@ -17,9 +17,22 @@
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="LineEdit" name="query"> <widget class="LineEdit" name="artist">
<property name="toolTip">
<string>Artist</string>
</property>
<property name="hint" stdset="0"> <property name="hint" stdset="0">
<string>Enter search terms here</string> <string>Artist</string>
</property>
</widget>
</item>
<item>
<widget class="LineEdit" name="album">
<property name="toolTip">
<string>Album</string>
</property>
<property name="hint" stdset="0">
<string>Album</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -99,7 +112,8 @@
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>query</tabstop> <tabstop>artist</tabstop>
<tabstop>album</tabstop>
<tabstop>search</tabstop> <tabstop>search</tabstop>
<tabstop>covers</tabstop> <tabstop>covers</tabstop>
<tabstop>buttonBox</tabstop> <tabstop>buttonBox</tabstop>
@ -139,7 +153,7 @@
</hints> </hints>
</connection> </connection>
<connection> <connection>
<sender>query</sender> <sender>artist</sender>
<signal>returnPressed()</signal> <signal>returnPressed()</signal>
<receiver>search</receiver> <receiver>search</receiver>
<slot>click()</slot> <slot>click()</slot>