mirror of
https://github.com/clementine-player/Clementine
synced 2024-12-16 19:31:02 +01:00
Load cover art from Magnatune: make AlbumCoverLoader load from http, and make the OSD load artwork asynchronously.
This commit is contained in:
parent
58da0a2f64
commit
41f306fd2a
@ -87,6 +87,7 @@ set(CLEMENTINE-SOURCES
|
||||
mergedproxymodel.cpp
|
||||
libraryfilterwidget.cpp
|
||||
radioviewcontainer.cpp
|
||||
networkaccessmanager.cpp
|
||||
)
|
||||
|
||||
# Header files that have Q_OBJECT in
|
||||
@ -159,6 +160,7 @@ set(CLEMENTINE-MOC-HEADERS
|
||||
mergedproxymodel.h
|
||||
libraryfilterwidget.h
|
||||
radioviewcontainer.h
|
||||
networkaccessmanager.h
|
||||
)
|
||||
|
||||
# lists of engine source files
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "albumcoverfetcher.h"
|
||||
#include "networkaccessmanager.h"
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QTimer>
|
||||
@ -25,9 +26,9 @@
|
||||
|
||||
const int AlbumCoverFetcher::kMaxConcurrentRequests = 5;
|
||||
|
||||
AlbumCoverFetcher::AlbumCoverFetcher(QNetworkAccessManager* network, QObject* parent)
|
||||
AlbumCoverFetcher::AlbumCoverFetcher(NetworkAccessManager* network, QObject* parent)
|
||||
: QObject(parent),
|
||||
network_(network),
|
||||
network_(network->network()),
|
||||
next_id_(0),
|
||||
request_starter_(new QTimer(this))
|
||||
{
|
||||
|
@ -30,11 +30,13 @@
|
||||
class QNetworkReply;
|
||||
class QString;
|
||||
|
||||
class NetworkAccessManager;
|
||||
|
||||
class AlbumCoverFetcher : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AlbumCoverFetcher(QNetworkAccessManager* network, QObject* parent = 0);
|
||||
AlbumCoverFetcher(NetworkAccessManager* network, QObject* parent = 0);
|
||||
virtual ~AlbumCoverFetcher() {}
|
||||
|
||||
static const int kMaxConcurrentRequests;
|
||||
|
@ -15,10 +15,13 @@
|
||||
*/
|
||||
|
||||
#include "albumcoverloader.h"
|
||||
#include "networkaccessmanager.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QDir>
|
||||
#include <QCoreApplication>
|
||||
#include <QUrl>
|
||||
#include <QNetworkReply>
|
||||
|
||||
const char* AlbumCoverLoader::kManuallyUnsetCover = "(unset)";
|
||||
|
||||
@ -26,7 +29,8 @@ AlbumCoverLoader::AlbumCoverLoader(QObject* parent)
|
||||
: QObject(parent),
|
||||
stop_requested_(false),
|
||||
height_(120),
|
||||
next_id_(0)
|
||||
next_id_(0),
|
||||
network_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@ -41,10 +45,11 @@ void AlbumCoverLoader::Clear() {
|
||||
}
|
||||
|
||||
quint64 AlbumCoverLoader::LoadImageAsync(const QString& art_automatic,
|
||||
const QString& art_manual) {
|
||||
const QString& art_manual) {
|
||||
Task task;
|
||||
task.art_automatic = art_automatic;
|
||||
task.art_manual = art_manual;
|
||||
task.state = State_TryingManual;
|
||||
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
@ -68,38 +73,93 @@ void AlbumCoverLoader::ProcessTasks() {
|
||||
task = tasks_.dequeue();
|
||||
}
|
||||
|
||||
// Try to load the image
|
||||
QImage image(TryLoadImage(task.art_automatic, task.art_manual));
|
||||
|
||||
if (!image.isNull()) {
|
||||
// Scale the image down
|
||||
image = image.scaled(QSize(height_, height_), Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
// Pad the image to height_ x height_
|
||||
QImage bigger_image(height_, height_, QImage::Format_ARGB32);
|
||||
bigger_image.fill(0);
|
||||
|
||||
QPainter p(&bigger_image);
|
||||
p.drawImage((height_ - image.width()) / 2, (height_ - image.height()) / 2,
|
||||
image);
|
||||
p.end();
|
||||
|
||||
image = bigger_image;
|
||||
}
|
||||
|
||||
emit ImageLoaded(task.id, image);
|
||||
ProcessTask(&task);
|
||||
}
|
||||
}
|
||||
|
||||
QImage AlbumCoverLoader::TryLoadImage(const QString &automatic, const QString &manual) {
|
||||
QImage ret;
|
||||
if (manual == kManuallyUnsetCover)
|
||||
return ret;
|
||||
if (!manual.isEmpty())
|
||||
ret.load(manual);
|
||||
if (!automatic.isEmpty() && ret.isNull())
|
||||
ret.load(automatic);
|
||||
return ret;
|
||||
void AlbumCoverLoader::ProcessTask(Task *task) {
|
||||
TryLoadResult result = TryLoadImage(*task);
|
||||
if (result.started_async) {
|
||||
// The image is being loaded from a remote URL, we'll carry on later
|
||||
// when it's done
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.loaded_success) {
|
||||
emit ImageLoaded(task->id, ScaleAndPad(result.image));
|
||||
return;
|
||||
}
|
||||
|
||||
NextState(task);
|
||||
}
|
||||
|
||||
void AlbumCoverLoader::NextState(Task* task) {
|
||||
if (task->state == State_TryingManual) {
|
||||
// Try the automatic one next
|
||||
task->state = State_TryingAuto;
|
||||
ProcessTask(task);
|
||||
} else {
|
||||
// Give up
|
||||
emit ImageLoaded(task->id, QImage());
|
||||
}
|
||||
}
|
||||
|
||||
AlbumCoverLoader::TryLoadResult AlbumCoverLoader::TryLoadImage(
|
||||
const Task& task) {
|
||||
QString filename;
|
||||
switch (task.state) {
|
||||
case State_TryingAuto: filename = task.art_automatic; break;
|
||||
case State_TryingManual: filename = task.art_manual; break;
|
||||
}
|
||||
|
||||
if (filename == kManuallyUnsetCover)
|
||||
return TryLoadResult(false, true, QImage());
|
||||
|
||||
if (filename.toLower().startsWith("http://")) {
|
||||
network_->Get(QUrl(filename), this, "RemoteFetchFinished", task.id, true);
|
||||
|
||||
remote_tasks_.insert(task.id, task);
|
||||
return TryLoadResult(true, false, QImage());
|
||||
}
|
||||
|
||||
QImage image(filename);
|
||||
return TryLoadResult(false, !image.isNull(), image);
|
||||
}
|
||||
|
||||
void AlbumCoverLoader::RemoteFetchFinished(quint64 id, QNetworkReply* reply) {
|
||||
reply->deleteLater();
|
||||
Task task = remote_tasks_.take(id);
|
||||
|
||||
if (reply->error() == QNetworkReply::NoError) {
|
||||
// Try to load the image
|
||||
QImage image;
|
||||
if (image.load(reply, 0)) {
|
||||
emit ImageLoaded(task.id, ScaleAndPad(image));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
NextState(&task);
|
||||
}
|
||||
|
||||
QImage AlbumCoverLoader::ScaleAndPad(const QImage &image) const {
|
||||
if (image.isNull())
|
||||
return image;
|
||||
|
||||
// Scale the image down
|
||||
QImage copy = image.scaled(QSize(height_, height_),
|
||||
Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
|
||||
// Pad the image to height_ x height_
|
||||
QImage bigger_image(height_, height_, QImage::Format_ARGB32);
|
||||
bigger_image.fill(0);
|
||||
|
||||
QPainter p(&bigger_image);
|
||||
p.drawImage((height_ - copy.width()) / 2, (height_ - copy.height()) / 2,
|
||||
copy);
|
||||
p.end();
|
||||
|
||||
return bigger_image;
|
||||
}
|
||||
|
||||
QPixmap AlbumCoverLoader::TryLoadPixmap(const QString &automatic, const QString &manual) {
|
||||
|
@ -24,11 +24,16 @@
|
||||
#include <QMutex>
|
||||
#include <QQueue>
|
||||
|
||||
class NetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
|
||||
class AlbumCoverLoader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AlbumCoverLoader(QObject* parent = 0);
|
||||
|
||||
void SetNetwork(NetworkAccessManager* network) { network_ = network; }
|
||||
|
||||
void Stop() { stop_requested_ = true; }
|
||||
|
||||
@ -39,7 +44,6 @@ class AlbumCoverLoader : public QObject {
|
||||
|
||||
void Clear();
|
||||
|
||||
static QImage TryLoadImage(const QString& automatic, const QString& manual);
|
||||
static QPixmap TryLoadPixmap(const QString& automatic, const QString& manual);
|
||||
|
||||
static const char* kManuallyUnsetCover;
|
||||
@ -49,21 +53,45 @@ class AlbumCoverLoader : public QObject {
|
||||
|
||||
private slots:
|
||||
void ProcessTasks();
|
||||
void RemoteFetchFinished(quint64 id, QNetworkReply* reply);
|
||||
|
||||
private:
|
||||
enum State {
|
||||
State_TryingManual,
|
||||
State_TryingAuto,
|
||||
};
|
||||
|
||||
struct Task {
|
||||
quint64 id;
|
||||
QString art_automatic;
|
||||
QString art_manual;
|
||||
State state;
|
||||
};
|
||||
|
||||
struct TryLoadResult {
|
||||
TryLoadResult(bool async, bool success, const QImage i)
|
||||
: started_async(async), loaded_success(success), image(i) {}
|
||||
|
||||
bool started_async;
|
||||
bool loaded_success;
|
||||
QImage image;
|
||||
};
|
||||
|
||||
void ProcessTask(Task* task);
|
||||
void NextState(Task* task);
|
||||
TryLoadResult TryLoadImage(const Task& task);
|
||||
QImage ScaleAndPad(const QImage& image) const;
|
||||
|
||||
bool stop_requested_;
|
||||
|
||||
int height_;
|
||||
|
||||
QMutex mutex_;
|
||||
QQueue<Task> tasks_;
|
||||
QMap<quint64, Task> remote_tasks_;
|
||||
quint64 next_id_;
|
||||
|
||||
NetworkAccessManager* network_;
|
||||
};
|
||||
|
||||
#endif // ALBUMCOVERLOADER_H
|
||||
|
@ -35,11 +35,12 @@
|
||||
|
||||
const char* AlbumCoverManager::kSettingsGroup = "CoverManager";
|
||||
|
||||
AlbumCoverManager::AlbumCoverManager(QNetworkAccessManager* network,
|
||||
AlbumCoverManager::AlbumCoverManager(NetworkAccessManager* network,
|
||||
LibraryBackend* backend, QWidget *parent)
|
||||
: QDialog(parent),
|
||||
constructed_(false),
|
||||
backend_(backend),
|
||||
network_(network),
|
||||
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this)),
|
||||
cover_fetcher_(new AlbumCoverFetcher(network, this)),
|
||||
artist_icon_(":/artist.png"),
|
||||
@ -117,6 +118,7 @@ void AlbumCoverManager::Init() {
|
||||
}
|
||||
|
||||
void AlbumCoverManager::CoverLoaderInitialised() {
|
||||
cover_loader_->Worker()->SetNetwork(network_);
|
||||
connect(cover_loader_->Worker().get(), SIGNAL(ImageLoaded(quint64,QImage)),
|
||||
SLOT(CoverImageLoaded(quint64,QImage)));
|
||||
}
|
||||
|
@ -28,13 +28,12 @@
|
||||
|
||||
class LibraryBackend;
|
||||
class AlbumCoverFetcher;
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class NetworkAccessManager;
|
||||
|
||||
class AlbumCoverManager : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
AlbumCoverManager(QNetworkAccessManager* network, LibraryBackend* backend,
|
||||
AlbumCoverManager(NetworkAccessManager* network, LibraryBackend* backend,
|
||||
QWidget *parent = 0);
|
||||
~AlbumCoverManager();
|
||||
|
||||
@ -100,6 +99,7 @@ class AlbumCoverManager : public QDialog {
|
||||
QAction* filter_with_covers_;
|
||||
QAction* filter_without_covers_;
|
||||
|
||||
NetworkAccessManager* network_;
|
||||
BackgroundThread<AlbumCoverLoader>* cover_loader_;
|
||||
QMap<quint64, QListWidgetItem*> cover_loading_tasks_;
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "lastfmstationdialog.h"
|
||||
#include "lastfmconfigdialog.h"
|
||||
#include "radiomodel.h"
|
||||
#include "networkaccessmanager.h"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
@ -59,7 +60,7 @@ LastFMService::LastFMService(RadioModel* parent)
|
||||
tag_list_(NULL),
|
||||
friends_list_(NULL),
|
||||
neighbours_list_(NULL),
|
||||
network_(parent->network())
|
||||
network_(parent->network()->network())
|
||||
{
|
||||
ReloadSettings();
|
||||
|
||||
|
@ -41,6 +41,7 @@ uint qHash(const lastfm::Track& track);
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
class QAction;
|
||||
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class LastFMService : public RadioService {
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "librarymodel.h"
|
||||
#include "librarybackend.h"
|
||||
#include "libraryfilterwidget.h"
|
||||
#include "networkaccessmanager.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
@ -49,7 +50,7 @@ MagnatuneService::MagnatuneService(RadioModel* parent)
|
||||
library_model_(new LibraryModel(library_backend_, this)),
|
||||
library_sort_model_(new QSortFilterProxyModel(this)),
|
||||
total_song_count_(0),
|
||||
network_(parent->network())
|
||||
network_(parent->network()->network())
|
||||
{
|
||||
connect(library_backend_, SIGNAL(TotalSongCountUpdated(int)),
|
||||
SLOT(UpdateTotalSongCount(int)));
|
||||
|
11
src/main.cpp
11
src/main.cpp
@ -30,14 +30,13 @@
|
||||
#include "commandlineoptions.h"
|
||||
#include "engines/enginebase.h"
|
||||
#include "config.h"
|
||||
#include "networkaccessmanager.h"
|
||||
|
||||
#include <QtSingleApplication>
|
||||
#include <QtDebug>
|
||||
#include <QLibraryInfo>
|
||||
#include <QTranslator>
|
||||
#include <QDir>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkDiskCache>
|
||||
|
||||
#include <glib/gutils.h>
|
||||
|
||||
@ -98,6 +97,8 @@ int main(int argc, char *argv[]) {
|
||||
qRegisterMetaType<Engine::SimpleMetaBundle>("Engine::SimpleMetaBundle");
|
||||
qRegisterMetaType<Equalizer::Params>("Equalizer::Params");
|
||||
qRegisterMetaTypeStreamOperators<Equalizer::Params>("Equalizer::Params");
|
||||
qRegisterMetaType<const char*>("const char*");
|
||||
qRegisterMetaType<QNetworkReply*>("QNetworkReply*");
|
||||
|
||||
|
||||
lastfm::ws::ApiKey = LastFMService::kApiKey;
|
||||
@ -135,11 +136,7 @@ int main(int argc, char *argv[]) {
|
||||
// Couldn't send the message so start anyway
|
||||
}
|
||||
|
||||
QNetworkAccessManager network;
|
||||
QNetworkDiskCache network_cache;
|
||||
network_cache.setCacheDirectory(QString("%1/.config/%2/networkcache/")
|
||||
.arg(QDir::homePath(), QCoreApplication::organizationName()));
|
||||
network.setCache(&network_cache);
|
||||
NetworkAccessManager network;
|
||||
|
||||
// MPRIS DBus interface.
|
||||
#ifdef Q_WS_X11
|
||||
|
@ -84,10 +84,10 @@ const char* MainWindow::kPlaylistFilterSpec =
|
||||
const char* MainWindow::kAllFilesFilterSpec =
|
||||
QT_TR_NOOP("All Files (*)");
|
||||
|
||||
MainWindow::MainWindow(QNetworkAccessManager* network, Engine::Type engine, QWidget *parent)
|
||||
MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidget *parent)
|
||||
: QMainWindow(parent),
|
||||
tray_icon_(new SystemTrayIcon(this)),
|
||||
osd_(new OSD(tray_icon_, this)),
|
||||
osd_(new OSD(tray_icon_, network, this)),
|
||||
track_slider_(new TrackSlider(this)),
|
||||
playlist_sequence_(new PlaylistSequence(this)),
|
||||
edit_tag_dialog_(new EditTagDialog),
|
||||
|
@ -49,17 +49,16 @@ class Equalizer;
|
||||
class CommandlineOptions;
|
||||
class TranscodeDialog;
|
||||
class Database;
|
||||
class NetworkAccessManager;
|
||||
|
||||
class QSortFilterProxyModel;
|
||||
class SystemTrayIcon;
|
||||
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindow(QNetworkAccessManager* network, Engine::Type engine, QWidget *parent = 0);
|
||||
MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
static const char* kSettingsGroup;
|
||||
|
61
src/networkaccessmanager.cpp
Normal file
61
src/networkaccessmanager.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include "networkaccessmanager.h"
|
||||
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkDiskCache>
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
NetworkAccessManager::NetworkAccessManager(QObject* parent)
|
||||
: QObject(parent),
|
||||
network_(new QNetworkAccessManager(this)),
|
||||
cache_(new QNetworkDiskCache(this))
|
||||
{
|
||||
cache_->setCacheDirectory(QString("%1/.config/%2/networkcache/")
|
||||
.arg(QDir::homePath(), QCoreApplication::organizationName()));
|
||||
network_->setCache(cache_);
|
||||
}
|
||||
|
||||
void NetworkAccessManager::Get(const QUrl &url, QObject *receiver,
|
||||
const char *slot, quint64 id, bool force_cache) {
|
||||
QMetaObject::invokeMethod(
|
||||
this, "RunGet", Qt::QueuedConnection,
|
||||
Q_ARG(QUrl, url), Q_ARG(QObject*, receiver),
|
||||
Q_ARG(const char*, slot),
|
||||
Q_ARG(quint64, id), Q_ARG(bool, force_cache));
|
||||
}
|
||||
|
||||
void NetworkAccessManager::RunGet(const QUrl &url, QObject *receiver,
|
||||
const char *slot, quint64 id, bool force_cache) {
|
||||
QNetworkRequest req(url);
|
||||
req.setRawHeader("User-Agent", QString("%1 %2").arg(
|
||||
QCoreApplication::applicationName(),
|
||||
QCoreApplication::applicationVersion()).toUtf8());
|
||||
|
||||
if (force_cache) {
|
||||
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
|
||||
QNetworkRequest::PreferCache);
|
||||
}
|
||||
|
||||
QNetworkReply* reply = network_->get(req);
|
||||
connect(reply, SIGNAL(finished()), SLOT(RequestFinished()));
|
||||
|
||||
Receiver r;
|
||||
r.receiver = receiver;
|
||||
r.slot = slot;
|
||||
r.id = id;
|
||||
|
||||
pending_replies_.insert(reply, r);
|
||||
}
|
||||
|
||||
void NetworkAccessManager::RequestFinished() {
|
||||
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
|
||||
Receiver r = pending_replies_.take(reply);
|
||||
|
||||
QMetaObject::invokeMethod(r.receiver, r.slot,
|
||||
Q_ARG(quint64, r.id),
|
||||
Q_ARG(QNetworkReply*, reply));
|
||||
}
|
45
src/networkaccessmanager.h
Normal file
45
src/networkaccessmanager.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef NETWORKACCESSMANAGER_H
|
||||
#define NETWORKACCESSMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkDiskCache;
|
||||
class QNetworkReply;
|
||||
class QUrl;
|
||||
|
||||
// It's like QNetworkAccessManager, but threadsafe, and sets our User-Agent
|
||||
// on all requests
|
||||
class NetworkAccessManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NetworkAccessManager(QObject* parent = 0);
|
||||
|
||||
// Only use this from the main thread
|
||||
QNetworkAccessManager* network() const { return network_; }
|
||||
|
||||
// Thread-safe. slot should take (quint64, QNetworkReply*)
|
||||
void Get(const QUrl& url, QObject* receiver, const char* slot,
|
||||
quint64 id, bool force_cache = false);
|
||||
|
||||
private slots:
|
||||
void RunGet(const QUrl& url, QObject* receiver, const char* slot,
|
||||
quint64 id, bool force_cache);
|
||||
void RequestFinished();
|
||||
|
||||
private:
|
||||
QNetworkAccessManager* network_;
|
||||
QNetworkDiskCache* cache_;
|
||||
|
||||
struct Receiver {
|
||||
QObject* receiver;
|
||||
const char* slot;
|
||||
quint64 id;
|
||||
};
|
||||
|
||||
QMap<QNetworkReply*, Receiver> pending_replies_;
|
||||
};
|
||||
|
||||
#endif // NETWORKACCESSMANAGER_H
|
39
src/osd.cpp
39
src/osd.cpp
@ -23,7 +23,7 @@
|
||||
|
||||
const char* OSD::kSettingsGroup = "OSD";
|
||||
|
||||
OSD::OSD(QSystemTrayIcon* tray_icon, QObject* parent)
|
||||
OSD::OSD(QSystemTrayIcon* tray_icon, NetworkAccessManager* network, QObject* parent)
|
||||
: QObject(parent),
|
||||
tray_icon_(tray_icon),
|
||||
timeout_msec_(5000),
|
||||
@ -32,8 +32,14 @@ OSD::OSD(QSystemTrayIcon* tray_icon, QObject* parent)
|
||||
show_art_(true),
|
||||
force_show_next_(false),
|
||||
ignore_next_stopped_(false),
|
||||
pretty_popup_(new OSDPretty(OSDPretty::Mode_Popup))
|
||||
pretty_popup_(new OSDPretty(OSDPretty::Mode_Popup)),
|
||||
network_(network),
|
||||
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this))
|
||||
{
|
||||
cover_loader_->Start();
|
||||
|
||||
connect(cover_loader_, SIGNAL(Initialised()), SLOT(CoverLoaderInitialised()));
|
||||
|
||||
ReloadSettings();
|
||||
Init();
|
||||
}
|
||||
@ -42,6 +48,12 @@ OSD::~OSD() {
|
||||
delete pretty_popup_;
|
||||
}
|
||||
|
||||
void OSD::CoverLoaderInitialised() {
|
||||
cover_loader_->Worker()->SetNetwork(network_);
|
||||
connect(cover_loader_->Worker().get(), SIGNAL(ImageLoaded(quint64,QImage)),
|
||||
SLOT(AlbumArtLoaded(quint64,QImage)));
|
||||
}
|
||||
|
||||
void OSD::ReloadSettings() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
@ -72,8 +84,27 @@ void OSD::SongChanged(const Song &song) {
|
||||
if (song.track() > 0)
|
||||
message_parts << tr("track %1").arg(song.track());
|
||||
|
||||
ShowMessage(summary, message_parts.join(", "), "notification-audio-play",
|
||||
show_art_ ? song.GetBestImage() : QImage());
|
||||
WaitingForAlbumArt waiting;
|
||||
waiting.icon = "notification-audio-play";
|
||||
waiting.summary = summary;
|
||||
waiting.message = message_parts.join(", ");
|
||||
|
||||
if (show_art_) {
|
||||
quint64 id = cover_loader_->Worker()->LoadImageAsync(
|
||||
song.art_automatic(), song.art_manual());
|
||||
waiting_for_album_art_.insert(id, waiting);
|
||||
} else {
|
||||
AlbumArtLoaded(waiting, QImage());
|
||||
}
|
||||
}
|
||||
|
||||
void OSD::AlbumArtLoaded(quint64 id, const QImage& image) {
|
||||
WaitingForAlbumArt info = waiting_for_album_art_.take(id);
|
||||
AlbumArtLoaded(info, image);
|
||||
}
|
||||
|
||||
void OSD::AlbumArtLoaded(const WaitingForAlbumArt info, const QImage& image) {
|
||||
ShowMessage(info.summary, info.message, info.icon, image);
|
||||
}
|
||||
|
||||
void OSD::Paused() {
|
||||
|
18
src/osd.h
18
src/osd.h
@ -23,8 +23,11 @@
|
||||
|
||||
#include "engines/engine_fwd.h"
|
||||
#include "song.h"
|
||||
#include "backgroundthread.h"
|
||||
#include "albumcoverloader.h"
|
||||
|
||||
class OSDPretty;
|
||||
class NetworkAccessManager;
|
||||
|
||||
class QDBusPendingCallWatcher;
|
||||
|
||||
@ -41,7 +44,7 @@ class OSD : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OSD(QSystemTrayIcon* tray_icon, QObject* parent = 0);
|
||||
OSD(QSystemTrayIcon* tray_icon, NetworkAccessManager* network, QObject* parent = 0);
|
||||
~OSD();
|
||||
|
||||
static const char* kSettingsGroup;
|
||||
@ -68,6 +71,12 @@ class OSD : public QObject {
|
||||
void VolumeChanged(int value);
|
||||
|
||||
private:
|
||||
struct WaitingForAlbumArt {
|
||||
QString summary;
|
||||
QString message;
|
||||
QString icon;
|
||||
};
|
||||
|
||||
void ShowMessage(const QString& summary,
|
||||
const QString& message = QString(),
|
||||
const QString& icon = QString(),
|
||||
@ -82,6 +91,9 @@ class OSD : public QObject {
|
||||
|
||||
private slots:
|
||||
void CallFinished(QDBusPendingCallWatcher* watcher);
|
||||
void CoverLoaderInitialised();
|
||||
void AlbumArtLoaded(quint64 id, const QImage& image);
|
||||
void AlbumArtLoaded(const WaitingForAlbumArt info, const QImage& image);
|
||||
|
||||
private:
|
||||
QSystemTrayIcon* tray_icon_;
|
||||
@ -95,6 +107,10 @@ class OSD : public QObject {
|
||||
|
||||
OSDPretty* pretty_popup_;
|
||||
|
||||
NetworkAccessManager* network_;
|
||||
BackgroundThread<AlbumCoverLoader>* cover_loader_;
|
||||
QMap<quint64, WaitingForAlbumArt> waiting_for_album_art_;
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
class GrowlNotificationWrapper;
|
||||
GrowlNotificationWrapper* wrapper_;
|
||||
|
@ -28,7 +28,7 @@
|
||||
|
||||
QMap<QString, RadioService*> RadioModel::sServices;
|
||||
|
||||
RadioModel::RadioModel(Database* db, QNetworkAccessManager* network, QObject* parent)
|
||||
RadioModel::RadioModel(Database* db, NetworkAccessManager* network, QObject* parent)
|
||||
: SimpleTreeModel<RadioItem>(new RadioItem(this), parent),
|
||||
db_(db),
|
||||
merged_model_(new MergedProxyModel(this)),
|
||||
|
@ -22,8 +22,7 @@
|
||||
#include "multiloadingindicator.h"
|
||||
#include "song.h"
|
||||
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class NetworkAccessManager;
|
||||
class RadioService;
|
||||
class LastFMService;
|
||||
class MergedProxyModel;
|
||||
@ -33,7 +32,7 @@ class RadioModel : public SimpleTreeModel<RadioItem> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RadioModel(Database* db, QNetworkAccessManager* network, QObject* parent = 0);
|
||||
RadioModel(Database* db, NetworkAccessManager* network, QObject* parent = 0);
|
||||
|
||||
enum {
|
||||
Role_Type = Qt::UserRole + 1,
|
||||
@ -59,7 +58,7 @@ class RadioModel : public SimpleTreeModel<RadioItem> {
|
||||
|
||||
Database* db() const { return db_; }
|
||||
MergedProxyModel* merged_model() const { return merged_model_; }
|
||||
QNetworkAccessManager* network() const { return network_; }
|
||||
NetworkAccessManager* network() const { return network_; }
|
||||
|
||||
signals:
|
||||
void TaskStarted(MultiLoadingIndicator::TaskType);
|
||||
@ -83,7 +82,7 @@ class RadioModel : public SimpleTreeModel<RadioItem> {
|
||||
static QMap<QString, RadioService*> sServices;
|
||||
Database* db_;
|
||||
MergedProxyModel* merged_model_;
|
||||
QNetworkAccessManager* network_;
|
||||
NetworkAccessManager* network_;
|
||||
};
|
||||
|
||||
#endif // RADIOMODEL_H
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "somafmservice.h"
|
||||
#include "radiomodel.h"
|
||||
#include "networkaccessmanager.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
@ -36,7 +37,7 @@ SomaFMService::SomaFMService(RadioModel* parent)
|
||||
: RadioService(kServiceName, parent),
|
||||
root_(NULL),
|
||||
context_menu_(new QMenu),
|
||||
network_(parent->network())
|
||||
network_(parent->network()->network())
|
||||
{
|
||||
context_menu_->addAction(QIcon(":media-playback-start.png"), tr("Add to playlist"), this, SLOT(AddToPlaylist()));
|
||||
context_menu_->addSeparator();
|
||||
|
11
src/song.cpp
11
src/song.cpp
@ -535,14 +535,3 @@ bool Song::Save() const {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QImage Song::GetBestImage() const {
|
||||
if (!d->image_.isNull())
|
||||
return d->image_;
|
||||
|
||||
QImage art(AlbumCoverLoader::TryLoadImage(d->art_automatic_, d->art_manual_));
|
||||
if (!art.isNull())
|
||||
return art;
|
||||
|
||||
return QImage(":/nocover.png");
|
||||
}
|
||||
|
@ -133,12 +133,6 @@ class Song {
|
||||
QString PrettyTitleWithArtist() const;
|
||||
QString PrettyLength() const;
|
||||
|
||||
// Loads and returns some album art for the song. Tries, in this order:
|
||||
// 1) An image set explicitly with set_image (eg. last.fm radio)
|
||||
// 2) An image set by the user with set_art_manual
|
||||
// 3) An image found by the library scanner
|
||||
QImage GetBestImage() const;
|
||||
|
||||
// Setters
|
||||
bool IsEditable() const { return d->valid_ && !d->filename_.isNull(); }
|
||||
bool Save() const;
|
||||
|
Loading…
Reference in New Issue
Block a user