initial commit of "multiple covers providers" feature:
- simple API for cover providers (both C++ and SIP) - a new "package" for cover related code
This commit is contained in:
parent
ff8110244f
commit
032b5f7e48
@ -56,8 +56,6 @@ set(SOURCES
|
||||
analyzers/sonogram.cpp
|
||||
analyzers/turbine.cpp
|
||||
|
||||
core/albumcoverloader.cpp
|
||||
core/artloader.cpp
|
||||
core/backgroundstreams.cpp
|
||||
core/backgroundthread.cpp
|
||||
core/commandlineoptions.cpp
|
||||
@ -71,7 +69,6 @@ set(SOURCES
|
||||
core/globalshortcutbackend.cpp
|
||||
core/globalshortcuts.cpp
|
||||
core/gnomeglobalshortcutbackend.cpp
|
||||
core/kittenloader.cpp
|
||||
core/mergedproxymodel.cpp
|
||||
core/musicstorage.cpp
|
||||
core/network.cpp
|
||||
@ -88,6 +85,15 @@ set(SOURCES
|
||||
core/taskmanager.cpp
|
||||
core/utilities.cpp
|
||||
|
||||
covers/albumcoverfetcher.cpp
|
||||
covers/albumcoverfetchersearch.cpp
|
||||
covers/albumcoverloader.cpp
|
||||
covers/artloader.cpp
|
||||
covers/coverprovider.cpp
|
||||
covers/coverproviders.cpp
|
||||
covers/kittenloader.cpp
|
||||
covers/lastfmcoverprovider.cpp
|
||||
|
||||
devices/connecteddevice.cpp
|
||||
devices/devicedatabasebackend.cpp
|
||||
devices/devicelister.cpp
|
||||
@ -268,8 +274,6 @@ set(HEADERS
|
||||
analyzers/sonogram.h
|
||||
analyzers/turbine.h
|
||||
|
||||
core/albumcoverloader.h
|
||||
core/artloader.h
|
||||
core/backgroundstreams.h
|
||||
core/backgroundthread.h
|
||||
core/crashreporting.h
|
||||
@ -278,7 +282,6 @@ set(HEADERS
|
||||
core/globalshortcuts.h
|
||||
core/globalshortcutbackend.h
|
||||
core/gnomeglobalshortcutbackend.h
|
||||
core/kittenloader.h
|
||||
core/mergedproxymodel.h
|
||||
core/mimedata.h
|
||||
core/network.h
|
||||
@ -287,6 +290,15 @@ set(HEADERS
|
||||
core/songloader.h
|
||||
core/taskmanager.h
|
||||
|
||||
covers/albumcoverfetcher.h
|
||||
covers/albumcoverfetchersearch.h
|
||||
covers/albumcoverloader.h
|
||||
covers/artloader.h
|
||||
covers/coverprovider.h
|
||||
covers/coverproviders.h
|
||||
covers/kittenloader.h
|
||||
covers/lastfmcoverprovider.h
|
||||
|
||||
devices/connecteddevice.h
|
||||
devices/devicedatabasebackend.h
|
||||
devices/devicelister.h
|
||||
@ -545,7 +557,6 @@ endif(ENABLE_VISUALISATIONS)
|
||||
# Lastfm
|
||||
if(HAVE_LIBLASTFM)
|
||||
list(APPEND SOURCES
|
||||
core/albumcoverfetcher.cpp
|
||||
radio/fixlastfm.cpp
|
||||
radio/lastfmconfig.cpp
|
||||
radio/lastfmservice.cpp
|
||||
@ -559,7 +570,6 @@ if(HAVE_LIBLASTFM)
|
||||
ui/albumcoversearcher.cpp
|
||||
)
|
||||
list(APPEND HEADERS
|
||||
core/albumcoverfetcher.h
|
||||
radio/lastfmconfig.h
|
||||
radio/lastfmservice.h
|
||||
radio/lastfmstationdialog.h
|
||||
@ -767,6 +777,7 @@ if(HAVE_SCRIPTING_PYTHON)
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementinePlaylistItemPtr.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementinePlaylistItemSpecialLoadResult.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementineTaskManagerTask.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100CoverSearchResult.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100Directory.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100LibraryBackendAlbum.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100PlaylistItemPtr.cpp
|
||||
|
@ -1,164 +0,0 @@
|
||||
/* 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 "albumcoverfetcher.h"
|
||||
#include "network.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QTimer>
|
||||
|
||||
#include <lastfm/Artist>
|
||||
#include <lastfm/XmlQuery>
|
||||
#include <lastfm/ws.h>
|
||||
|
||||
const int AlbumCoverFetcher::kMaxConcurrentRequests = 5;
|
||||
|
||||
AlbumCoverFetcher::AlbumCoverFetcher(QObject* parent, QNetworkAccessManager* network)
|
||||
: QObject(parent),
|
||||
network_(network ? network : new NetworkAccessManager(this)),
|
||||
next_id_(0),
|
||||
request_starter_(new QTimer(this))
|
||||
{
|
||||
request_starter_->setInterval(1000);
|
||||
connect(request_starter_, SIGNAL(timeout()), SLOT(StartRequests()));
|
||||
}
|
||||
|
||||
quint64 AlbumCoverFetcher::FetchAlbumCover(
|
||||
const QString& artist_name, const QString& album_name) {
|
||||
QueuedRequest request;
|
||||
request.query = artist_name + " " + album_name;
|
||||
request.search = false;
|
||||
request.id = next_id_ ++;
|
||||
|
||||
AddRequest(request);
|
||||
return request.id;
|
||||
}
|
||||
|
||||
quint64 AlbumCoverFetcher::SearchForCovers(const QString &query) {
|
||||
QueuedRequest request;
|
||||
request.query = query;
|
||||
request.search = true;
|
||||
request.id = next_id_ ++;
|
||||
|
||||
AddRequest(request);
|
||||
return request.id;
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::AddRequest(QueuedRequest req) {
|
||||
queued_requests_.enqueue(req);
|
||||
|
||||
if (!request_starter_->isActive())
|
||||
request_starter_->start();
|
||||
|
||||
if (active_requests_.count() < kMaxConcurrentRequests)
|
||||
StartRequests();
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::Clear() {
|
||||
queued_requests_.clear();
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::StartRequests() {
|
||||
if (queued_requests_.isEmpty()) {
|
||||
request_starter_->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
while (!queued_requests_.isEmpty() &&
|
||||
active_requests_.count() < kMaxConcurrentRequests) {
|
||||
QueuedRequest request = queued_requests_.dequeue();
|
||||
|
||||
QMap<QString, QString> params;
|
||||
params["method"] = "album.search";
|
||||
params["album"] = request.query;
|
||||
|
||||
QNetworkReply* reply = lastfm::ws::post(params);
|
||||
connect(reply, SIGNAL(finished()), SLOT(AlbumSearchFinished()));
|
||||
active_requests_.insert(reply, request);
|
||||
}
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::AlbumSearchFinished() {
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
reply->deleteLater();
|
||||
QueuedRequest request = active_requests_.take(reply);
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
// TODO: retry request.
|
||||
emit AlbumCoverFetched(request.id, QImage());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
lastfm::XmlQuery query(lastfm::ws::parse(reply));
|
||||
#ifdef Q_OS_WIN32
|
||||
if (lastfm::ws::last_parse_error != lastfm::ws::NoError)
|
||||
throw std::runtime_error("");
|
||||
#endif
|
||||
|
||||
// Parse the list of search results
|
||||
QList<lastfm::XmlQuery> elements = query["results"]["albummatches"].children("album");
|
||||
SearchResults results;
|
||||
foreach (const lastfm::XmlQuery& element, elements) {
|
||||
SearchResult result;
|
||||
result.album = element["name"].text();
|
||||
result.artist = element["artist"].text();
|
||||
result.image_url = element["image size=extralarge"].text();
|
||||
results << result;
|
||||
}
|
||||
|
||||
// If we only wanted to do the search then we're done
|
||||
if (request.search) {
|
||||
emit SearchFinished(request.id, results);
|
||||
return;
|
||||
}
|
||||
|
||||
// No results?
|
||||
if (results.isEmpty()) {
|
||||
emit AlbumCoverFetched(request.id, QImage());
|
||||
return;
|
||||
}
|
||||
|
||||
// Now we need to fetch the first result's image
|
||||
QNetworkReply* image_reply = network_->get(QNetworkRequest(results[0].image_url));
|
||||
connect(image_reply, SIGNAL(finished()), SLOT(AlbumCoverFetchFinished()));
|
||||
|
||||
active_requests_[image_reply] = request;
|
||||
} catch (std::runtime_error&) {
|
||||
if (request.search)
|
||||
emit SearchFinished(request.id, AlbumCoverFetcher::SearchResults());
|
||||
else
|
||||
emit AlbumCoverFetched(request.id, QImage());
|
||||
}
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::AlbumCoverFetchFinished() {
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
reply->deleteLater();
|
||||
QueuedRequest request = active_requests_.take(reply);
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
// TODO: retry request.
|
||||
emit AlbumCoverFetched(request.id, QImage());
|
||||
return;
|
||||
}
|
||||
|
||||
QImage image;
|
||||
image.loadFromData(reply->readAll());
|
||||
|
||||
emit AlbumCoverFetched(request.id, image);
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
#include "mpris.h"
|
||||
#include "mpris1.h"
|
||||
#include "mpris2.h"
|
||||
#include "core/artloader.h"
|
||||
#include "covers/artloader.h"
|
||||
|
||||
namespace mpris {
|
||||
|
||||
|
@ -15,9 +15,9 @@
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "artloader.h"
|
||||
#include "mpris1.h"
|
||||
#include "mpris_common.h"
|
||||
#include "covers/artloader.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusConnection>
|
||||
|
@ -19,11 +19,11 @@
|
||||
#include "mpris_common.h"
|
||||
#include "mpris1.h"
|
||||
#include "mpris2.h"
|
||||
#include "core/artloader.h"
|
||||
#include "core/mpris2_player.h"
|
||||
#include "core/mpris2_root.h"
|
||||
#include "core/mpris2_tracklist.h"
|
||||
#include "core/player.h"
|
||||
#include "covers/artloader.h"
|
||||
#include "engines/enginebase.h"
|
||||
#include "playlist/playlist.h"
|
||||
#include "playlist/playlistmanager.h"
|
||||
|
@ -24,8 +24,8 @@
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include "config.h"
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "core/song.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "engines/engine_fwd.h"
|
||||
#include "playlist/playlistitem.h"
|
||||
|
||||
|
@ -65,10 +65,9 @@
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
using boost::scoped_ptr;
|
||||
|
||||
#include "albumcoverloader.h"
|
||||
#include "encoding.h"
|
||||
#include "utilities.h"
|
||||
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "engines/enginebase.h"
|
||||
#include "library/sqlrow.h"
|
||||
#include "widgets/trackslider.h"
|
||||
|
106
src/covers/albumcoverfetcher.cpp
Normal file
106
src/covers/albumcoverfetcher.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
/* 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 "albumcoverfetcher.h"
|
||||
#include "albumcoverfetchersearch.h"
|
||||
#include "core/network.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
const int AlbumCoverFetcher::kMaxConcurrentRequests = 5;
|
||||
|
||||
AlbumCoverFetcher::AlbumCoverFetcher(QObject* parent, QNetworkAccessManager* network)
|
||||
: QObject(parent),
|
||||
network_(network ? network : new NetworkAccessManager(this)),
|
||||
next_id_(0),
|
||||
request_starter_(new QTimer(this))
|
||||
{
|
||||
request_starter_->setInterval(1000);
|
||||
connect(request_starter_, SIGNAL(timeout()), SLOT(StartRequests()));
|
||||
}
|
||||
|
||||
quint64 AlbumCoverFetcher::FetchAlbumCover(
|
||||
const QString& artist_name, const QString& album_name) {
|
||||
CoverSearchRequest request;
|
||||
request.query = artist_name + " " + album_name;
|
||||
request.search = false;
|
||||
request.id = next_id_ ++;
|
||||
|
||||
AddRequest(request);
|
||||
return request.id;
|
||||
}
|
||||
|
||||
quint64 AlbumCoverFetcher::SearchForCovers(const QString &query) {
|
||||
CoverSearchRequest request;
|
||||
request.query = query;
|
||||
request.search = true;
|
||||
request.id = next_id_ ++;
|
||||
|
||||
AddRequest(request);
|
||||
return request.id;
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::AddRequest(const CoverSearchRequest& req) {
|
||||
queued_requests_.enqueue(req);
|
||||
|
||||
if (!request_starter_->isActive())
|
||||
request_starter_->start();
|
||||
|
||||
if (active_requests_.size() < kMaxConcurrentRequests)
|
||||
StartRequests();
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::Clear() {
|
||||
queued_requests_.clear();
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::StartRequests() {
|
||||
if (queued_requests_.isEmpty()) {
|
||||
request_starter_->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
while (!queued_requests_.isEmpty() &&
|
||||
active_requests_.size() < kMaxConcurrentRequests) {
|
||||
|
||||
CoverSearchRequest request = queued_requests_.dequeue();
|
||||
|
||||
// search objects are this fetcher's children so worst case scenario - they get
|
||||
// deleted with it
|
||||
AlbumCoverFetcherSearch* search = new AlbumCoverFetcherSearch(request, network_,
|
||||
this);
|
||||
active_requests_.insert(request.id, search);
|
||||
|
||||
connect(search, SIGNAL(SearchFinished(quint64, CoverSearchResults)),
|
||||
SLOT(SingleSearchFinished(quint64, CoverSearchResults)));
|
||||
connect(search, SIGNAL(AlbumCoverFetched(quint64, const QImage&)),
|
||||
SLOT(SingleCoverFetched(quint64, const QImage&)));
|
||||
|
||||
search->Start();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::SingleSearchFinished(quint64 request_id, CoverSearchResults results) {
|
||||
delete active_requests_.take(request_id);
|
||||
emit SearchFinished(request_id, results);
|
||||
}
|
||||
|
||||
void AlbumCoverFetcher::SingleCoverFetched(quint64 request_id, const QImage& image) {
|
||||
delete active_requests_.take(request_id);
|
||||
emit AlbumCoverFetched(request_id, image);
|
||||
}
|
@ -18,21 +18,48 @@
|
||||
#ifndef ALBUMCOVERFETCHER_H
|
||||
#define ALBUMCOVERFETCHER_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QImage>
|
||||
#include <QMap>
|
||||
#include <QList>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
|
||||
#include <lastfm/Album>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
class QNetworkReply;
|
||||
class QString;
|
||||
|
||||
class AlbumCoverFetcherSearch;
|
||||
|
||||
// This class represents a single search-for-cover request. It identifies
|
||||
// and describes the request.
|
||||
struct CoverSearchRequest {
|
||||
// an unique (for one AlbumCoverFetcher) request identifier
|
||||
quint64 id;
|
||||
// a search query
|
||||
QString query;
|
||||
// is this only a search request or should we also fetch the first
|
||||
// cover that's found?
|
||||
bool search;
|
||||
};
|
||||
|
||||
// This structure represents a single result of some album's cover search request.
|
||||
// It contains an URL that leads to a found cover plus it's description (usually
|
||||
// the "artist - album" string).
|
||||
struct CoverSearchResult {
|
||||
// description of this result (we suggest using the "artist - album" format)
|
||||
QString description;
|
||||
// an URL of a cover image described by this CoverSearchResult
|
||||
QString image_url;
|
||||
};
|
||||
|
||||
// This is a complete result of a single search request (a list of results, each
|
||||
// describing one image, actually).
|
||||
typedef QList<CoverSearchResult> CoverSearchResults;
|
||||
|
||||
// This class searches for album covers for a given query or artist/album and
|
||||
// returns URLs.
|
||||
// returns URLs. It's NOT thread-safe.
|
||||
class AlbumCoverFetcher : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
@ -40,13 +67,6 @@ class AlbumCoverFetcher : public QObject {
|
||||
AlbumCoverFetcher(QObject* parent = 0, QNetworkAccessManager* network = 0);
|
||||
virtual ~AlbumCoverFetcher() {}
|
||||
|
||||
struct SearchResult {
|
||||
QString artist;
|
||||
QString album;
|
||||
QString image_url;
|
||||
};
|
||||
typedef QList<SearchResult> SearchResults;
|
||||
|
||||
static const int kMaxConcurrentRequests;
|
||||
|
||||
quint64 SearchForCovers(const QString& query);
|
||||
@ -56,27 +76,21 @@ class AlbumCoverFetcher : public QObject {
|
||||
|
||||
signals:
|
||||
void AlbumCoverFetched(quint64, const QImage& cover);
|
||||
void SearchFinished(quint64, const AlbumCoverFetcher::SearchResults& results);
|
||||
void SearchFinished(quint64, const CoverSearchResults& results);
|
||||
|
||||
private slots:
|
||||
void AlbumSearchFinished();
|
||||
void AlbumCoverFetchFinished();
|
||||
void SingleSearchFinished(quint64, CoverSearchResults results);
|
||||
void SingleCoverFetched(quint64, const QImage& cover);
|
||||
void StartRequests();
|
||||
|
||||
private:
|
||||
struct QueuedRequest {
|
||||
quint64 id;
|
||||
QString query;
|
||||
bool search;
|
||||
};
|
||||
|
||||
void AddRequest(const QueuedRequest req);
|
||||
void AddRequest(const CoverSearchRequest& req);
|
||||
|
||||
QNetworkAccessManager* network_;
|
||||
quint64 next_id_;
|
||||
|
||||
QQueue<QueuedRequest> queued_requests_;
|
||||
QMap<QNetworkReply*, QueuedRequest> active_requests_;
|
||||
QQueue<CoverSearchRequest> queued_requests_;
|
||||
QHash<quint64, AlbumCoverFetcherSearch*> active_requests_;
|
||||
|
||||
QTimer* request_starter_;
|
||||
};
|
117
src/covers/albumcoverfetchersearch.cpp
Normal file
117
src/covers/albumcoverfetchersearch.cpp
Normal file
@ -0,0 +1,117 @@
|
||||
/* 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 "albumcoverfetcher.h"
|
||||
#include "albumcoverfetchersearch.h"
|
||||
#include "coverprovider.h"
|
||||
#include "coverproviders.h"
|
||||
|
||||
#include <QMutexLocker>
|
||||
#include <QNetworkReply>
|
||||
|
||||
const int AlbumCoverFetcherSearch::kSearchTimeout = 10000;
|
||||
|
||||
AlbumCoverFetcherSearch::AlbumCoverFetcherSearch(const CoverSearchRequest& request,
|
||||
QNetworkAccessManager* network,
|
||||
QObject* parent)
|
||||
: QObject(parent),
|
||||
request_(request),
|
||||
network_(network)
|
||||
{
|
||||
// we will terminate the search after kSearchTimeout miliseconds if we are not
|
||||
// able to find any results before that point in time
|
||||
startTimer(kSearchTimeout);
|
||||
}
|
||||
|
||||
void AlbumCoverFetcherSearch::timerEvent(QTimerEvent* event) {
|
||||
Q_UNUSED(event);
|
||||
|
||||
if(request_.search) {
|
||||
emit SearchFinished(request_.id, CoverSearchResults());
|
||||
} else {
|
||||
emit AlbumCoverFetched(request_.id, QImage());
|
||||
}
|
||||
}
|
||||
|
||||
void AlbumCoverFetcherSearch::Start() {
|
||||
QList<CoverProvider*> providers_list = CoverProviders::instance().List();
|
||||
providers_left_ = providers_list.size();
|
||||
|
||||
foreach(CoverProvider* provider, providers_list) {
|
||||
QNetworkReply* reply = provider->SendRequest(request_.query);
|
||||
|
||||
connect(reply, SIGNAL(finished()), SLOT(ProviderSearchFinished()));
|
||||
providers_.insert(reply, provider);
|
||||
}
|
||||
}
|
||||
|
||||
void AlbumCoverFetcherSearch::ProviderSearchFinished() {
|
||||
{
|
||||
QMutexLocker locker(&search_mutex_);
|
||||
Q_UNUSED(locker);
|
||||
|
||||
providers_left_--;
|
||||
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
if(reply->error() == QNetworkReply::NoError) {
|
||||
CoverProvider* provider = providers_.take(reply);
|
||||
|
||||
CoverSearchResults partial_results = provider->ParseReply(reply);
|
||||
// add results from the current provider to our pool
|
||||
results_.append(partial_results);
|
||||
}
|
||||
|
||||
// do we have more providers left?
|
||||
if(providers_left_) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we only wanted to do the search then we're done
|
||||
if (request_.search) {
|
||||
emit SearchFinished(request_.id, results_);
|
||||
return;
|
||||
}
|
||||
|
||||
// no results?
|
||||
if (results_.isEmpty()) {
|
||||
emit AlbumCoverFetched(request_.id, QImage());
|
||||
return;
|
||||
}
|
||||
|
||||
// now we need to fetch the first result's image
|
||||
QNetworkReply* image_reply = network_->get(QNetworkRequest(results_[0].image_url));
|
||||
connect(image_reply, SIGNAL(finished()), SLOT(ProviderCoverFetchFinished()));
|
||||
}
|
||||
|
||||
void AlbumCoverFetcherSearch::ProviderCoverFetchFinished() {
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
reply->deleteLater();
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
// TODO: retry request.
|
||||
emit AlbumCoverFetched(request_.id, QImage());
|
||||
|
||||
} else {
|
||||
QImage image;
|
||||
image.loadFromData(reply->readAll());
|
||||
|
||||
emit AlbumCoverFetched(request_.id, image);
|
||||
}
|
||||
}
|
81
src/covers/albumcoverfetchersearch.h
Normal file
81
src/covers/albumcoverfetchersearch.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* 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 ALBUMCOVERFETCHERSEARCH_H
|
||||
#define ALBUMCOVERFETCHERSEARCH_H
|
||||
|
||||
#include "albumcoverfetcher.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
|
||||
class CoverProvider;
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
class QTimerEvent;
|
||||
|
||||
// This class encapsulates a single search for covers initiated by an AlbumCoverFetcher.
|
||||
// The search engages all of the known cover providers. AlbumCoverFetcherSearch signals
|
||||
// search results to an interested AlbumCoverFetcher when all of the providers have done
|
||||
// their part.
|
||||
class AlbumCoverFetcherSearch : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// A timeout (in miliseconds) for every search.
|
||||
static const int kSearchTimeout;
|
||||
|
||||
AlbumCoverFetcherSearch(const CoverSearchRequest& request, QNetworkAccessManager* network,
|
||||
QObject* parent);
|
||||
virtual ~AlbumCoverFetcherSearch() {}
|
||||
|
||||
// Starts the search. This is the moment when we count cover providers available
|
||||
// in the application.
|
||||
void Start();
|
||||
|
||||
signals:
|
||||
// It's the end of search (when there was no fetch-me-a-cover request).
|
||||
void SearchFinished(quint64, CoverSearchResults results);
|
||||
// It's the end of search and we've fetched a cover.
|
||||
void AlbumCoverFetched(quint64, const QImage& cover);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent* event);
|
||||
|
||||
private slots:
|
||||
void ProviderSearchFinished();
|
||||
void ProviderCoverFetchFinished();
|
||||
|
||||
private:
|
||||
// Search request encapsulated by this AlbumCoverFetcherSearch.
|
||||
CoverSearchRequest request_;
|
||||
// Complete results (from all of the available providers).
|
||||
CoverSearchResults results_;
|
||||
|
||||
// We initialize this in the Start() method.
|
||||
// When this reaches 0, the search is over and appropriate signal
|
||||
// is emitted.
|
||||
int providers_left_;
|
||||
QMap<QNetworkReply*, CoverProvider*> providers_;
|
||||
|
||||
QNetworkAccessManager* network_;
|
||||
|
||||
QMutex search_mutex_;
|
||||
};
|
||||
|
||||
#endif // ALBUMCOVERFETCHERSEARCH_H
|
@ -16,8 +16,8 @@
|
||||
*/
|
||||
|
||||
#include "albumcoverloader.h"
|
||||
#include "network.h"
|
||||
#include "utilities.h"
|
||||
#include "core/network.h"
|
||||
#include "core/utilities.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QDir>
|
@ -18,8 +18,8 @@
|
||||
#ifndef ALBUMCOVERLOADER_H
|
||||
#define ALBUMCOVERLOADER_H
|
||||
|
||||
#include "backgroundthread.h"
|
||||
#include "song.h"
|
||||
#include "core/backgroundthread.h"
|
||||
#include "core/song.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QImage>
|
@ -18,8 +18,8 @@
|
||||
#ifndef ARTLOADER_H
|
||||
#define ARTLOADER_H
|
||||
|
||||
#include "backgroundthread.h"
|
||||
#include "song.h"
|
||||
#include "core/backgroundthread.h"
|
||||
#include "core/song.h"
|
||||
|
||||
#include <QObject>
|
||||
|
24
src/covers/coverprovider.cpp
Normal file
24
src/covers/coverprovider.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
/* 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 "coverprovider.h"
|
||||
|
||||
CoverProvider::CoverProvider(const QString& name, QObject* parent)
|
||||
: QObject(parent),
|
||||
name_(name)
|
||||
{
|
||||
}
|
59
src/covers/coverprovider.h
Normal file
59
src/covers/coverprovider.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 COVERPROVIDER_H
|
||||
#define COVERPROVIDER_H
|
||||
|
||||
#include "albumcoverfetcher.h"
|
||||
#include "coverproviders.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
// Each implementation of this interface downloads covers from one online
|
||||
// service. There are no limitations on what this service might be - last.fm,
|
||||
// Amazon, Google Images - you name it.
|
||||
class CoverProvider : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CoverProvider(const QString& name, QObject* parent = &CoverProviders::instance());
|
||||
virtual ~CoverProvider() {}
|
||||
|
||||
// A name (very short description) of this provider, like "last.fm".
|
||||
QString name() const { return name_; }
|
||||
|
||||
// Given a search request from Clementine, provider has to create and invoke
|
||||
// a NetworkRequest. It then has to return a corresponding NetworkReply,
|
||||
// without connecting to it's finished() signal!
|
||||
// Responsibilities of provider:
|
||||
// - maps the given query to a NetworkRequest that a service this provider
|
||||
// uses will understand
|
||||
// - makes the prepared request and returns the resulting reply
|
||||
virtual QNetworkReply* SendRequest(const QString& query) = 0;
|
||||
|
||||
// Provider parses a reply which is now filled with data obtained from a service
|
||||
// this provider communicates with. The result is a QList of CoverSearchResult
|
||||
// objects.
|
||||
virtual CoverSearchResults ParseReply(QNetworkReply* reply) = 0;
|
||||
|
||||
private:
|
||||
QString name_;
|
||||
};
|
||||
|
||||
#endif // COVERPROVIDER_H
|
63
src/covers/coverproviders.cpp
Normal file
63
src/covers/coverproviders.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
/* 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 "coverprovider.h"
|
||||
#include "coverproviders.h"
|
||||
#include "lastfmcoverprovider.h"
|
||||
|
||||
CoverProviders::CoverProviders()
|
||||
{
|
||||
// registering built-in providers...
|
||||
|
||||
// every built-in provider needs an explicit parent; otherwise,
|
||||
// the default parent, namely CoverProviders::instance(), will
|
||||
// cause an infinite recursion here
|
||||
cover_providers_.append(new LastFmCoverProvider(this));
|
||||
}
|
||||
|
||||
void CoverProviders::AddCoverProvider(CoverProvider* provider) {
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
Q_UNUSED(locker);
|
||||
|
||||
cover_providers_.append(provider);
|
||||
connect(provider, SIGNAL(destroyed()), SLOT(RemoveCoverProvider()));
|
||||
}
|
||||
}
|
||||
|
||||
void CoverProviders::RemoveCoverProvider() {
|
||||
// qobject_cast doesn't work here with providers created by python
|
||||
CoverProvider* provider = static_cast<CoverProvider*>(sender());
|
||||
|
||||
if (provider) {
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
Q_UNUSED(locker);
|
||||
|
||||
cover_providers_.removeAll(provider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QList<CoverProvider*> CoverProviders::List() {
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
Q_UNUSED(locker);
|
||||
|
||||
return QList<CoverProvider*>(cover_providers_);
|
||||
}
|
||||
}
|
59
src/covers/coverproviders.h
Normal file
59
src/covers/coverproviders.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 COVERPROVIDERS_H
|
||||
#define COVERPROVIDERS_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
|
||||
class CoverProvider;
|
||||
|
||||
// This is a singleton, a global repository for cover providers. Each one of those has to register
|
||||
// with CoverProviders instance by invoking "CoverProviders::instance().AddCoverProvider(this)".
|
||||
// Providers are automatically unregistered from the repository when they are deleted.
|
||||
// The class is thread safe except for the initialization.
|
||||
class CoverProviders : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// This performs lazy initialization of the CoverProviders which is not thread-safe!
|
||||
static CoverProviders& instance() {
|
||||
static CoverProviders instance_;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
// Let's a cover provider to register itself in the repository.
|
||||
void AddCoverProvider(CoverProvider* provider);
|
||||
// Returns a list of the currently registered cover providers.
|
||||
const QList<CoverProvider*> List();
|
||||
|
||||
~CoverProviders() {}
|
||||
|
||||
private slots:
|
||||
void RemoveCoverProvider();
|
||||
|
||||
private:
|
||||
CoverProviders();
|
||||
CoverProviders(CoverProviders const&);
|
||||
void operator=(CoverProviders const&);
|
||||
|
||||
QList<CoverProvider*> cover_providers_;
|
||||
QMutex mutex_;
|
||||
};
|
||||
|
||||
#endif // COVERPROVIDERS_H
|
@ -1,11 +1,10 @@
|
||||
#include "kittenloader.h"
|
||||
#include "core/network.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include "core/network.h"
|
||||
|
||||
const char* KittenLoader::kFlickrKittenUrl =
|
||||
"http://api.flickr.com/services/rest/"
|
||||
"?method=flickr.photos.search"
|
66
src/covers/lastfmcoverprovider.cpp
Normal file
66
src/covers/lastfmcoverprovider.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
/* 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 "albumcoverfetcher.h"
|
||||
#include "coverprovider.h"
|
||||
#include "lastfmcoverprovider.h"
|
||||
|
||||
#include <lastfm/Artist>
|
||||
#include <lastfm/XmlQuery>
|
||||
#include <lastfm/ws.h>
|
||||
|
||||
#include <QNetworkReply>
|
||||
|
||||
LastFmCoverProvider::LastFmCoverProvider(QObject* parent)
|
||||
: CoverProvider("last.fm", parent)
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkReply* LastFmCoverProvider::SendRequest(const QString& query) {
|
||||
QMap<QString, QString> params;
|
||||
params["method"] = "album.search";
|
||||
params["album"] = query;
|
||||
|
||||
return lastfm::ws::post(params);
|
||||
}
|
||||
|
||||
CoverSearchResults LastFmCoverProvider::ParseReply(QNetworkReply* reply) {
|
||||
CoverSearchResults results;
|
||||
|
||||
try {
|
||||
lastfm::XmlQuery query(lastfm::ws::parse(reply));
|
||||
#ifdef Q_OS_WIN32
|
||||
if (lastfm::ws::last_parse_error != lastfm::ws::NoError)
|
||||
return results;
|
||||
#endif
|
||||
|
||||
// parse the list of search results
|
||||
QList<lastfm::XmlQuery> elements = query["results"]["albummatches"].children("album");
|
||||
|
||||
foreach (const lastfm::XmlQuery& element, elements) {
|
||||
CoverSearchResult result;
|
||||
result.description = element["artist"].text() + " - " + element["name"].text();
|
||||
result.image_url = element["image size=extralarge"].text();
|
||||
results << result;
|
||||
}
|
||||
|
||||
return results;
|
||||
|
||||
} catch(std::runtime_error&) {
|
||||
return results;
|
||||
}
|
||||
}
|
38
src/covers/lastfmcoverprovider.h
Normal file
38
src/covers/lastfmcoverprovider.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* 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 LASTFMCOVERPROVIDER_H
|
||||
#define LASTFMCOVERPROVIDER_H
|
||||
|
||||
#include "albumcoverfetcher.h"
|
||||
#include "coverprovider.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
// A built-in cover provider which fetches covers from last.fm.
|
||||
class LastFmCoverProvider : public CoverProvider {
|
||||
public:
|
||||
LastFmCoverProvider(QObject* parent);
|
||||
virtual ~LastFmCoverProvider() {}
|
||||
|
||||
QNetworkReply* SendRequest(const QString& query);
|
||||
CoverSearchResults ParseReply(QNetworkReply* reply);
|
||||
};
|
||||
|
||||
#endif // LASTFMCOVERPROVIDER_H
|
@ -21,9 +21,9 @@
|
||||
#include "librarydirectorymodel.h"
|
||||
#include "libraryview.h"
|
||||
#include "sqlrow.h"
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "core/database.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "playlist/songmimedata.h"
|
||||
#include "smartplaylists/generator.h"
|
||||
#include "smartplaylists/generatormimedata.h"
|
||||
|
@ -24,7 +24,6 @@
|
||||
#endif // Q_OS_WIN32
|
||||
|
||||
#include "config.h"
|
||||
#include "core/artloader.h"
|
||||
#include "core/commandlineoptions.h"
|
||||
#include "core/crashreporting.h"
|
||||
#include "core/database.h"
|
||||
@ -37,6 +36,8 @@
|
||||
#include "core/song.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "core/utilities.h"
|
||||
#include "covers/artloader.h"
|
||||
#include "covers/coverproviders.h"
|
||||
#include "engines/enginebase.h"
|
||||
#include "library/directory.h"
|
||||
#include "playlist/playlist.h"
|
||||
@ -307,6 +308,10 @@ int main(int argc, char *argv[]) {
|
||||
PlaylistManager playlists(&task_manager, NULL);
|
||||
RadioModel radio_model(database.get(), &task_manager, NULL);
|
||||
|
||||
// Initialize the repository of cover providers to avoid race conditions
|
||||
// later
|
||||
CoverProviders::instance();
|
||||
|
||||
// Get the last.fm service if it's available
|
||||
LastFMService* lastfm_service = NULL;
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
|
27
src/scripting/python/albumcoverfetcher.sip
Normal file
27
src/scripting/python/albumcoverfetcher.sip
Normal file
@ -0,0 +1,27 @@
|
||||
struct CoverSearchResult {
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "covers/albumcoverfetcher.h"
|
||||
%End
|
||||
|
||||
%Docstring
|
||||
Represents a single result of some album's cover search request.
|
||||
|
||||
It contains an URL that leads to a found cover plus it's description (usually
|
||||
the "artist - album" string).
|
||||
%End
|
||||
|
||||
QString description;
|
||||
%Docstring
|
||||
Description of this result.
|
||||
|
||||
We suggest using the "artist - album" format.
|
||||
%End
|
||||
|
||||
QString image_url;
|
||||
%Docstring
|
||||
An URL of a cover image described by this CoverSearchResult
|
||||
%End
|
||||
};
|
||||
|
||||
typedef QList<CoverSearchResult> CoverSearchResults;
|
@ -5,6 +5,9 @@
|
||||
%Import QtNetwork/QtNetworkmod.sip
|
||||
|
||||
%Include autoexpandingtreeview.sip
|
||||
%Include albumcoverfetcher.sip
|
||||
%Include coverprovider.sip
|
||||
%Include coverproviders.sip
|
||||
%Include directory.sip
|
||||
%Include engine_fwd.sip
|
||||
%Include iconloader.sip
|
||||
|
58
src/scripting/python/coverprovider.sip
Normal file
58
src/scripting/python/coverprovider.sip
Normal file
@ -0,0 +1,58 @@
|
||||
class CoverProvider : QObject {
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "covers/albumcoverfetcher.h"
|
||||
#include "covers/coverprovider.h"
|
||||
%End
|
||||
|
||||
%Docstring
|
||||
Each implementation of this interface downloads covers from one online
|
||||
service.
|
||||
|
||||
There are no limitations on what this service might be - last.fm, Amazon,
|
||||
Google Images - you name it.
|
||||
|
||||
CoverProvider should be stateless since it will be used in multi-threaded
|
||||
environment. It can and probably should use some contextual information
|
||||
though because a single search spans through two interface methods invoked
|
||||
independently. The key to connecting both invocations is the NetworkReply
|
||||
object.
|
||||
|
||||
A flow of single request for covers:
|
||||
- Provider is asked to prepare and invoke a NetworkRequest (L{SendRequest()})
|
||||
for a given cover query. Provider then returns a corresponding NetworkReply.
|
||||
It should not connect to finished() signal of the reply, though!
|
||||
- Later, in a separate invocation (L{ParseReply()}), provider will be asked to
|
||||
parse the NetworkReply it once prepared. The result is a QList of L{CoverSearchResult}
|
||||
objects.
|
||||
|
||||
Every CoverProvider has a name which should describe (in a word or two) the service
|
||||
it's using.
|
||||
%End
|
||||
|
||||
public:
|
||||
CoverProvider(const QString& name);
|
||||
virtual ~CoverProvider();
|
||||
|
||||
QString name();
|
||||
%Docstring
|
||||
Name of this provider, like "last.fm".
|
||||
%End
|
||||
|
||||
virtual QNetworkReply* SendRequest(const QString& query) = 0;
|
||||
%Docstring
|
||||
Given a search request from Clementine, provider has to create and invoke
|
||||
a NetworkRequest.
|
||||
|
||||
It then has to return a corresponding NetworkReply, without connecting to
|
||||
it's finished() signal!
|
||||
%End
|
||||
|
||||
virtual CoverSearchResults ParseReply(QNetworkReply* reply) = 0;
|
||||
%Docstring
|
||||
Provider parses a reply which is now filled with data obtained from a service
|
||||
this provider communicates with. The result is a QList of L{CoverSearchResult}
|
||||
objects.
|
||||
%End
|
||||
|
||||
};
|
27
src/scripting/python/coverproviders.sip
Normal file
27
src/scripting/python/coverproviders.sip
Normal file
@ -0,0 +1,27 @@
|
||||
class CoverProviders /NoDefaultCtors/ {
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "covers/coverprovider.h"
|
||||
#include "covers/coverproviders.h"
|
||||
#include "scripting/python/pythonengine.h"
|
||||
%End
|
||||
|
||||
%Docstring
|
||||
This is a global repository for cover providers.
|
||||
|
||||
Each one of those has to register with CoverProviders instance by invoking
|
||||
"CoverProviders::instance().AddCoverProvider(this)". Providers are automatically
|
||||
unregistered from the repository when they are deleted.
|
||||
%End
|
||||
|
||||
public:
|
||||
void AddCoverProvider(CoverProvider* provider /Transfer/);
|
||||
%MethodCode
|
||||
sipCpp->AddCoverProvider(a0);
|
||||
PythonEngine::instance()->RegisterNativeObject(a0);
|
||||
%End
|
||||
%Docstring
|
||||
Let's a cover provider to register itself in the repository.
|
||||
%End
|
||||
|
||||
};
|
@ -22,6 +22,7 @@
|
||||
#include "pythonengine.h"
|
||||
#include "pythonscript.h"
|
||||
#include "sipAPIclementine.h"
|
||||
#include "covers/coverproviders.h"
|
||||
#include "library/library.h"
|
||||
|
||||
#include <QFile>
|
||||
@ -138,6 +139,7 @@ bool PythonEngine::EnsureInitialised() {
|
||||
AddObject(manager()->data().radio_model_, sipType_RadioModel, "radio_model");
|
||||
AddObject(manager()->data().settings_dialog_, sipType_SettingsDialog, "settings_dialog");
|
||||
AddObject(manager()->data().task_manager_, sipType_TaskManager, "task_manager");
|
||||
AddObject(&CoverProviders::instance(), sipType_CoverProviders, "cover_providers");
|
||||
}
|
||||
|
||||
AddObject(manager()->ui(), sipType_UIInterface, "ui");
|
||||
|
@ -32,6 +32,8 @@ appropriately.
|
||||
const char *name;
|
||||
sipTypeDef **type;
|
||||
} list[] = {
|
||||
CLASS(CoverProvider),
|
||||
CLASS(CoverProviders),
|
||||
CLASS(LibraryBackend),
|
||||
CLASS(MergedProxyModel),
|
||||
CLASS(NetworkAccessManager),
|
||||
|
@ -16,7 +16,8 @@
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "covers/albumcoverfetcher.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "library/librarybackend.h"
|
||||
#include "ui/albumcoverchoicecontroller.h"
|
||||
#include "ui/albumcovermanager.h"
|
||||
@ -25,7 +26,6 @@
|
||||
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
# include "ui/albumcoversearcher.h"
|
||||
# include "core/albumcoverfetcher.h"
|
||||
#endif
|
||||
|
||||
#include <QAction>
|
||||
@ -52,8 +52,8 @@ AlbumCoverChoiceController::AlbumCoverChoiceController(QWidget* parent)
|
||||
: QWidget(parent),
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
cover_searcher_(new AlbumCoverSearcher(QIcon(":/nocover.png"), this)),
|
||||
cover_fetcher_(new AlbumCoverFetcher(this)),
|
||||
#endif
|
||||
cover_fetcher_(new AlbumCoverFetcher(this)),
|
||||
save_file_dialog_(NULL),
|
||||
cover_from_url_dialog_(NULL),
|
||||
library_(NULL)
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "albumcoversearcher.h"
|
||||
#include "iconloader.h"
|
||||
#include "ui_albumcovermanager.h"
|
||||
#include "core/albumcoverfetcher.h"
|
||||
#include "covers/albumcoverfetcher.h"
|
||||
#include "library/librarybackend.h"
|
||||
#include "library/libraryquery.h"
|
||||
#include "library/sqlrow.h"
|
||||
|
@ -24,9 +24,9 @@
|
||||
|
||||
#include "gtest/gtest_prod.h"
|
||||
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "core/backgroundthread.h"
|
||||
#include "core/song.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
|
||||
class AlbumCoverChoiceController;
|
||||
class AlbumCoverFetcher;
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
#include "albumcoversearcher.h"
|
||||
#include "ui_albumcoversearcher.h"
|
||||
#include "core/albumcoverfetcher.h"
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "covers/albumcoverfetcher.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
|
||||
#include <QKeyEvent>
|
||||
#include <QListWidgetItem>
|
||||
@ -52,7 +52,7 @@ AlbumCoverSearcher::~AlbumCoverSearcher() {
|
||||
void AlbumCoverSearcher::Init(AlbumCoverFetcher* fetcher) {
|
||||
fetcher_ = fetcher;
|
||||
|
||||
connect(fetcher_, SIGNAL(SearchFinished(quint64,AlbumCoverFetcher::SearchResults)), SLOT(SearchFinished(quint64,AlbumCoverFetcher::SearchResults)));
|
||||
connect(fetcher_, SIGNAL(SearchFinished(quint64,CoverSearchResults)), SLOT(SearchFinished(quint64,CoverSearchResults)));
|
||||
}
|
||||
|
||||
QImage AlbumCoverSearcher::Exec(const QString &query) {
|
||||
@ -84,7 +84,7 @@ void AlbumCoverSearcher::Search() {
|
||||
id_ = fetcher_->SearchForCovers(ui_->query->text());
|
||||
}
|
||||
|
||||
void AlbumCoverSearcher::SearchFinished(quint64 id, const AlbumCoverFetcher::SearchResults &results) {
|
||||
void AlbumCoverSearcher::SearchFinished(quint64 id, const CoverSearchResults& results) {
|
||||
if (id != id_)
|
||||
return;
|
||||
|
||||
@ -95,7 +95,7 @@ void AlbumCoverSearcher::SearchFinished(quint64 id, const AlbumCoverFetcher::Sea
|
||||
|
||||
ui_->covers->clear();
|
||||
cover_loading_tasks_.clear();
|
||||
foreach (const AlbumCoverFetcher::SearchResult& result, results) {
|
||||
foreach (const CoverSearchResult& result, results) {
|
||||
if (result.image_url.isEmpty())
|
||||
continue;
|
||||
|
||||
@ -103,7 +103,7 @@ void AlbumCoverSearcher::SearchFinished(quint64 id, const AlbumCoverFetcher::Sea
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem(ui_->covers);
|
||||
item->setIcon(no_cover_icon_);
|
||||
item->setText(result.artist + " - " + result.album);
|
||||
item->setText(result.description);
|
||||
item->setData(Role_ImageURL, result.image_url);
|
||||
item->setData(Role_ImageRequestId, id);
|
||||
item->setData(Qt::TextAlignmentRole, QVariant(Qt::AlignTop | Qt::AlignHCenter));
|
||||
|
@ -18,8 +18,8 @@
|
||||
#ifndef ALBUMCOVERSEARCHER_H
|
||||
#define ALBUMCOVERSEARCHER_H
|
||||
|
||||
#include "core/albumcoverfetcher.h"
|
||||
#include "core/backgroundthread.h"
|
||||
#include "covers/albumcoverfetcher.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QIcon>
|
||||
@ -54,7 +54,7 @@ protected:
|
||||
|
||||
private slots:
|
||||
void Search();
|
||||
void SearchFinished(quint64 id, const AlbumCoverFetcher::SearchResults& results);
|
||||
void SearchFinished(quint64 id, const CoverSearchResults& results);
|
||||
void ImageLoaded(quint64 id, const QImage& image);
|
||||
|
||||
void CoverDoubleClicked(const QModelIndex& index);
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
#include "coverfromurldialog.h"
|
||||
#include "ui_coverfromurldialog.h"
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "core/network.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
|
||||
#include <QImage>
|
||||
#include <QMessageBox>
|
||||
|
@ -19,8 +19,8 @@
|
||||
#include "edittagdialog.h"
|
||||
#include "trackselectiondialog.h"
|
||||
#include "ui_edittagdialog.h"
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "core/utilities.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "library/library.h"
|
||||
#include "library/librarybackend.h"
|
||||
#include "musicbrainz/fingerprinter.h"
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "core/artloader.h"
|
||||
#include "core/backgroundstreams.h"
|
||||
#include "core/commandlineoptions.h"
|
||||
#include "core/database.h"
|
||||
@ -34,6 +33,7 @@
|
||||
#include "core/stylesheetloader.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "core/utilities.h"
|
||||
#include "covers/artloader.h"
|
||||
#include "devices/devicemanager.h"
|
||||
#include "devices/devicestatefiltermodel.h"
|
||||
#include "devices/deviceview.h"
|
||||
|
@ -17,8 +17,8 @@
|
||||
|
||||
#include "fullscreenhypnotoad.h"
|
||||
#include "nowplayingwidget.h"
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "core/kittenloader.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "covers/kittenloader.h"
|
||||
#include "library/librarybackend.h"
|
||||
#include "ui/albumcoverchoicecontroller.h"
|
||||
#include "ui/iconloader.h"
|
||||
|
@ -24,9 +24,9 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "engines/engine_fwd.h"
|
||||
#include "core/albumcoverloader.h"
|
||||
#include "core/backgroundthread.h"
|
||||
#include "core/song.h"
|
||||
#include "covers/albumcoverloader.h"
|
||||
#include "playlist/playlistsequence.h"
|
||||
|
||||
class OrgFreedesktopNotificationsInterface;
|
||||
|
Loading…
x
Reference in New Issue
Block a user