diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 86c3e2320..526ca2cac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/core/albumcoverfetcher.cpp b/src/core/albumcoverfetcher.cpp deleted file mode 100644 index 7c1f08e30..000000000 --- a/src/core/albumcoverfetcher.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* 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 "albumcoverfetcher.h" -#include "network.h" - -#include -#include - -#include -#include -#include - -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 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(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 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(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); -} diff --git a/src/core/mpris.cpp b/src/core/mpris.cpp index 984df55ea..b6ecdb850 100644 --- a/src/core/mpris.cpp +++ b/src/core/mpris.cpp @@ -18,7 +18,7 @@ #include "mpris.h" #include "mpris1.h" #include "mpris2.h" -#include "core/artloader.h" +#include "covers/artloader.h" namespace mpris { diff --git a/src/core/mpris1.cpp b/src/core/mpris1.cpp index 873efb27a..2338de865 100644 --- a/src/core/mpris1.cpp +++ b/src/core/mpris1.cpp @@ -15,9 +15,9 @@ along with Clementine. If not, see . */ -#include "artloader.h" #include "mpris1.h" #include "mpris_common.h" +#include "covers/artloader.h" #include #include diff --git a/src/core/mpris2.cpp b/src/core/mpris2.cpp index f125f5a49..90fbdd7d3 100644 --- a/src/core/mpris2.cpp +++ b/src/core/mpris2.cpp @@ -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" diff --git a/src/core/player.h b/src/core/player.h index 230b36615..e5b1f0f6f 100644 --- a/src/core/player.h +++ b/src/core/player.h @@ -24,8 +24,8 @@ #include #include "config.h" -#include "core/albumcoverloader.h" #include "core/song.h" +#include "covers/albumcoverloader.h" #include "engines/engine_fwd.h" #include "playlist/playlistitem.h" diff --git a/src/core/song.cpp b/src/core/song.cpp index d7bbea50e..5e8382b4b 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -65,10 +65,9 @@ #include 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" diff --git a/src/covers/albumcoverfetcher.cpp b/src/covers/albumcoverfetcher.cpp new file mode 100644 index 000000000..24f2a3083 --- /dev/null +++ b/src/covers/albumcoverfetcher.cpp @@ -0,0 +1,106 @@ +/* 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 "albumcoverfetcher.h" +#include "albumcoverfetchersearch.h" +#include "core/network.h" + +#include + +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); +} diff --git a/src/core/albumcoverfetcher.h b/src/covers/albumcoverfetcher.h similarity index 53% rename from src/core/albumcoverfetcher.h rename to src/covers/albumcoverfetcher.h index bfa533fc0..f174f3f9a 100644 --- a/src/core/albumcoverfetcher.h +++ b/src/covers/albumcoverfetcher.h @@ -18,21 +18,48 @@ #ifndef ALBUMCOVERFETCHER_H #define ALBUMCOVERFETCHER_H +#include #include -#include +#include #include #include #include -#include - #include 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 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 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 queued_requests_; - QMap active_requests_; + QQueue queued_requests_; + QHash active_requests_; QTimer* request_starter_; }; diff --git a/src/covers/albumcoverfetchersearch.cpp b/src/covers/albumcoverfetchersearch.cpp new file mode 100644 index 000000000..b8c00f29a --- /dev/null +++ b/src/covers/albumcoverfetchersearch.cpp @@ -0,0 +1,117 @@ +/* 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 "albumcoverfetcher.h" +#include "albumcoverfetchersearch.h" +#include "coverprovider.h" +#include "coverproviders.h" + +#include +#include + +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 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(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(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); + } +} diff --git a/src/covers/albumcoverfetchersearch.h b/src/covers/albumcoverfetchersearch.h new file mode 100644 index 000000000..5662af391 --- /dev/null +++ b/src/covers/albumcoverfetchersearch.h @@ -0,0 +1,81 @@ +/* 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 ALBUMCOVERFETCHERSEARCH_H +#define ALBUMCOVERFETCHERSEARCH_H + +#include "albumcoverfetcher.h" + +#include +#include +#include + +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 providers_; + + QNetworkAccessManager* network_; + + QMutex search_mutex_; +}; + +#endif // ALBUMCOVERFETCHERSEARCH_H diff --git a/src/core/albumcoverloader.cpp b/src/covers/albumcoverloader.cpp similarity index 99% rename from src/core/albumcoverloader.cpp rename to src/covers/albumcoverloader.cpp index 145dc0010..4f8814979 100644 --- a/src/core/albumcoverloader.cpp +++ b/src/covers/albumcoverloader.cpp @@ -16,8 +16,8 @@ */ #include "albumcoverloader.h" -#include "network.h" -#include "utilities.h" +#include "core/network.h" +#include "core/utilities.h" #include #include diff --git a/src/core/albumcoverloader.h b/src/covers/albumcoverloader.h similarity index 98% rename from src/core/albumcoverloader.h rename to src/covers/albumcoverloader.h index 45b2cfbf0..bbaa42bd5 100644 --- a/src/core/albumcoverloader.h +++ b/src/covers/albumcoverloader.h @@ -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 #include diff --git a/src/core/artloader.cpp b/src/covers/artloader.cpp similarity index 100% rename from src/core/artloader.cpp rename to src/covers/artloader.cpp diff --git a/src/core/artloader.h b/src/covers/artloader.h similarity index 96% rename from src/core/artloader.h rename to src/covers/artloader.h index c6b8f71e8..fb07319c5 100644 --- a/src/core/artloader.h +++ b/src/covers/artloader.h @@ -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 diff --git a/src/covers/coverprovider.cpp b/src/covers/coverprovider.cpp new file mode 100644 index 000000000..10738baed --- /dev/null +++ b/src/covers/coverprovider.cpp @@ -0,0 +1,24 @@ +/* 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 "coverprovider.h" + +CoverProvider::CoverProvider(const QString& name, QObject* parent) + : QObject(parent), + name_(name) +{ +} diff --git a/src/covers/coverprovider.h b/src/covers/coverprovider.h new file mode 100644 index 000000000..65f54a4c7 --- /dev/null +++ b/src/covers/coverprovider.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 COVERPROVIDER_H +#define COVERPROVIDER_H + +#include "albumcoverfetcher.h" +#include "coverproviders.h" + +#include + +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 diff --git a/src/covers/coverproviders.cpp b/src/covers/coverproviders.cpp new file mode 100644 index 000000000..d449bcc8f --- /dev/null +++ b/src/covers/coverproviders.cpp @@ -0,0 +1,63 @@ +/* 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 "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(sender()); + + if (provider) { + { + QMutexLocker locker(&mutex_); + Q_UNUSED(locker); + + cover_providers_.removeAll(provider); + } + } +} + +const QList CoverProviders::List() { + { + QMutexLocker locker(&mutex_); + Q_UNUSED(locker); + + return QList(cover_providers_); + } +} diff --git a/src/covers/coverproviders.h b/src/covers/coverproviders.h new file mode 100644 index 000000000..698481133 --- /dev/null +++ b/src/covers/coverproviders.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 COVERPROVIDERS_H +#define COVERPROVIDERS_H + +#include +#include + +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 List(); + + ~CoverProviders() {} + +private slots: + void RemoveCoverProvider(); + +private: + CoverProviders(); + CoverProviders(CoverProviders const&); + void operator=(CoverProviders const&); + + QList cover_providers_; + QMutex mutex_; +}; + +#endif // COVERPROVIDERS_H diff --git a/src/core/kittenloader.cpp b/src/covers/kittenloader.cpp similarity index 99% rename from src/core/kittenloader.cpp rename to src/covers/kittenloader.cpp index b7c921821..97964e22d 100644 --- a/src/core/kittenloader.cpp +++ b/src/covers/kittenloader.cpp @@ -1,11 +1,10 @@ #include "kittenloader.h" +#include "core/network.h" #include #include #include -#include "core/network.h" - const char* KittenLoader::kFlickrKittenUrl = "http://api.flickr.com/services/rest/" "?method=flickr.photos.search" diff --git a/src/core/kittenloader.h b/src/covers/kittenloader.h similarity index 100% rename from src/core/kittenloader.h rename to src/covers/kittenloader.h diff --git a/src/covers/lastfmcoverprovider.cpp b/src/covers/lastfmcoverprovider.cpp new file mode 100644 index 000000000..1c4640cea --- /dev/null +++ b/src/covers/lastfmcoverprovider.cpp @@ -0,0 +1,66 @@ +/* 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 "albumcoverfetcher.h" +#include "coverprovider.h" +#include "lastfmcoverprovider.h" + +#include +#include +#include + +#include + +LastFmCoverProvider::LastFmCoverProvider(QObject* parent) + : CoverProvider("last.fm", parent) +{ +} + +QNetworkReply* LastFmCoverProvider::SendRequest(const QString& query) { + QMap 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 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; + } +} diff --git a/src/covers/lastfmcoverprovider.h b/src/covers/lastfmcoverprovider.h new file mode 100644 index 000000000..1ebe1852f --- /dev/null +++ b/src/covers/lastfmcoverprovider.h @@ -0,0 +1,38 @@ +/* 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 LASTFMCOVERPROVIDER_H +#define LASTFMCOVERPROVIDER_H + +#include "albumcoverfetcher.h" +#include "coverprovider.h" + +#include + +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 diff --git a/src/library/librarymodel.cpp b/src/library/librarymodel.cpp index 33a1efc7d..a80de9dc8 100644 --- a/src/library/librarymodel.cpp +++ b/src/library/librarymodel.cpp @@ -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" diff --git a/src/main.cpp b/src/main.cpp index ca2abf1ea..571f376b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 diff --git a/src/scripting/python/albumcoverfetcher.sip b/src/scripting/python/albumcoverfetcher.sip new file mode 100644 index 000000000..3fcf67a13 --- /dev/null +++ b/src/scripting/python/albumcoverfetcher.sip @@ -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 CoverSearchResults; diff --git a/src/scripting/python/clementine.sip b/src/scripting/python/clementine.sip index adb9f42e0..b6bce81d7 100644 --- a/src/scripting/python/clementine.sip +++ b/src/scripting/python/clementine.sip @@ -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 diff --git a/src/scripting/python/coverprovider.sip b/src/scripting/python/coverprovider.sip new file mode 100644 index 000000000..5ce59efca --- /dev/null +++ b/src/scripting/python/coverprovider.sip @@ -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 + +}; diff --git a/src/scripting/python/coverproviders.sip b/src/scripting/python/coverproviders.sip new file mode 100644 index 000000000..4597a30d0 --- /dev/null +++ b/src/scripting/python/coverproviders.sip @@ -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 + +}; diff --git a/src/scripting/python/pythonengine.cpp b/src/scripting/python/pythonengine.cpp index 2a1647d91..31e3b7c37 100644 --- a/src/scripting/python/pythonengine.cpp +++ b/src/scripting/python/pythonengine.cpp @@ -22,6 +22,7 @@ #include "pythonengine.h" #include "pythonscript.h" #include "sipAPIclementine.h" +#include "covers/coverproviders.h" #include "library/library.h" #include @@ -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"); diff --git a/src/scripting/python/scriptinterface.sip b/src/scripting/python/scriptinterface.sip index 5490f970c..ede7712ee 100644 --- a/src/scripting/python/scriptinterface.sip +++ b/src/scripting/python/scriptinterface.sip @@ -32,6 +32,8 @@ appropriately. const char *name; sipTypeDef **type; } list[] = { + CLASS(CoverProvider), + CLASS(CoverProviders), CLASS(LibraryBackend), CLASS(MergedProxyModel), CLASS(NetworkAccessManager), diff --git a/src/ui/albumcoverchoicecontroller.cpp b/src/ui/albumcoverchoicecontroller.cpp index b20d001c0..22a422698 100644 --- a/src/ui/albumcoverchoicecontroller.cpp +++ b/src/ui/albumcoverchoicecontroller.cpp @@ -16,7 +16,8 @@ along with Clementine. If not, see . */ -#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 @@ -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) diff --git a/src/ui/albumcovermanager.cpp b/src/ui/albumcovermanager.cpp index eb3fd8440..3994d7468 100644 --- a/src/ui/albumcovermanager.cpp +++ b/src/ui/albumcovermanager.cpp @@ -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" diff --git a/src/ui/albumcovermanager.h b/src/ui/albumcovermanager.h index cdbb820c7..5afb18c2b 100644 --- a/src/ui/albumcovermanager.h +++ b/src/ui/albumcovermanager.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; diff --git a/src/ui/albumcoversearcher.cpp b/src/ui/albumcoversearcher.cpp index 8fe507594..87c7639a6 100644 --- a/src/ui/albumcoversearcher.cpp +++ b/src/ui/albumcoversearcher.cpp @@ -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 #include @@ -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)); diff --git a/src/ui/albumcoversearcher.h b/src/ui/albumcoversearcher.h index f361e726e..0116cdf74 100644 --- a/src/ui/albumcoversearcher.h +++ b/src/ui/albumcoversearcher.h @@ -18,8 +18,8 @@ #ifndef ALBUMCOVERSEARCHER_H #define ALBUMCOVERSEARCHER_H -#include "core/albumcoverfetcher.h" #include "core/backgroundthread.h" +#include "covers/albumcoverfetcher.h" #include #include @@ -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); diff --git a/src/ui/coverfromurldialog.cpp b/src/ui/coverfromurldialog.cpp index 680dbbd0c..6f0ac47ad 100644 --- a/src/ui/coverfromurldialog.cpp +++ b/src/ui/coverfromurldialog.cpp @@ -17,8 +17,8 @@ #include "coverfromurldialog.h" #include "ui_coverfromurldialog.h" -#include "core/albumcoverloader.h" #include "core/network.h" +#include "covers/albumcoverloader.h" #include #include diff --git a/src/ui/edittagdialog.cpp b/src/ui/edittagdialog.cpp index c87f65de8..ad1ee5d76 100644 --- a/src/ui/edittagdialog.cpp +++ b/src/ui/edittagdialog.cpp @@ -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" diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index 3ccaae03e..8769a9d76 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -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" diff --git a/src/widgets/nowplayingwidget.cpp b/src/widgets/nowplayingwidget.cpp index 7773c0956..abf47a7c1 100644 --- a/src/widgets/nowplayingwidget.cpp +++ b/src/widgets/nowplayingwidget.cpp @@ -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" diff --git a/src/widgets/osd.h b/src/widgets/osd.h index c1f4969d0..06b44747e 100644 --- a/src/widgets/osd.h +++ b/src/widgets/osd.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;