Commit d26b763d authored by Michael Pyne's avatar Michael Pyne

Fix cover art downloading by using KIO async, the way it's meant to be used. ...

Fix cover art downloading by using KIO async, the way it's meant to be used.  A notification is used to
let the user know when the cover art is actually done downloading.

Also, some minor cleanups.

svn path=/trunk/KDE/kdemultimedia/juk/; revision=933994
parent 54a946b3
......@@ -44,6 +44,7 @@ set(juk_SRCS ${tunepimp_SRCS}
covericonview.cpp
coverinfo.cpp
covermanager.cpp
coverproxy.cpp
dbuscollectionproxy.cpp
deletedialog.cpp
directorylist.cpp
......@@ -130,7 +131,7 @@ install(TARGETS juk ${INSTALL_TARGETS_DEFAULT_ARGS} )
########### install files ###############
install( FILES juk.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} )
install( FILES jukui.rc jukui-rtl.rc DESTINATION ${DATA_INSTALL_DIR}/juk )
install( FILES juk.notifyrc jukui.rc jukui-rtl.rc DESTINATION ${DATA_INSTALL_DIR}/juk )
install( FILES jukservicemenu.desktop DESTINATION
${SERVICES_INSTALL_DIR}/ServiceMenus )
install( FILES org.kde.juk.collection.xml org.kde.juk.player.xml org.kde.juk.search.xml DESTINATION ${DBUS_INTERFACES_INSTALL_DIR} )
......
......@@ -39,8 +39,6 @@
#include "actioncollection.h"
#include "tag.h"
#include "viewmode.h"
#include "coverinfo.h"
#include "covermanager.h"
using ActionCollection::action;
......@@ -411,8 +409,6 @@ void CollectionListItem::refresh()
playlist()->slotWeightDirty(i);
}
file().coverInfo()->setCover();
if(listView()->isVisible())
repaint();
......
......@@ -25,6 +25,7 @@
#include <QHash>
#include <QPixmapCache>
#include <QByteArray>
#include <QMap>
#include <kdebug.h>
#include <ktemporaryfile.h>
......@@ -32,7 +33,10 @@
#include <kurl.h>
#include <kstandarddirs.h>
#include <kglobal.h>
#include <kio/netaccess.h>
#include <kio/job.h>
#include "juk.h"
#include "coverproxy.h"
// This is a dictionary to map the track path to their ID. Otherwise we'd have
// to store this info with each CollectionListItem, which would break the cache
......@@ -106,12 +110,15 @@ public:
/// Maps file names to coverKey id's.
TrackLookupMap tracks;
/// A map of outstanding download KJobs to their coverKey
QMap<KJob*, coverKey> downloadJobs;
/// A static pixmap cache is maintained for covers, with key format of:
/// 'f' followed by the pathname for FullSize covers, and
/// 't' followed by the pathname for Thumbnail covers.
/// However only thumbnails are currently cached.
CoverManagerPrivate() : m_timer(new CoverSaveHelper(0))
CoverManagerPrivate() : m_timer(new CoverSaveHelper(0)), m_coverProxy(0)
{
loadCovers();
}
......@@ -119,6 +126,7 @@ public:
~CoverManagerPrivate()
{
delete m_timer;
delete m_coverProxy;
saveCovers();
}
......@@ -143,6 +151,12 @@ public:
void saveCovers() const;
CoverProxy *coverProxy() {
if(!m_coverProxy)
m_coverProxy = new CoverProxy;
return m_coverProxy;
}
private:
void loadCovers();
......@@ -153,6 +167,8 @@ public:
QString coverLocation() const;
CoverSaveHelper *m_timer;
CoverProxy *m_coverProxy;
};
// This is responsible for making sure that the CoverManagerPrivate class
......@@ -384,7 +400,11 @@ QPixmap CoverManager::coverFromData(const CoverData &coverData, Size size)
// full size pics is not really useful as they are infrequently shown.
if(size == Thumbnail) {
pix = pix.scaled(80, 80, Qt::KeepAspectRatio, Qt::SmoothTransformation);
// Double scale is faster and 99% as accurate
QSize newSize(pix.size());
newSize.scale(80, 80, Qt::KeepAspectRatio);
pix = pix.scaled(2 * newSize)
.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
QPixmapCache::insert(path, pix);
}
......@@ -437,28 +457,65 @@ coverKey CoverManager::addCover(const KUrl &path, const QString &artist, const Q
kDebug() << "Saving pixmap to " << coverData->path;
data()->createDataDir();
coverData->artist = artist.toLower();
coverData->album = album.toLower();
coverData->refCount = 0;
data()->covers[id] = coverData;
// Can't use NetAccess::download() since if path is already a local file
// (which is possible) then that function will return without copying, since
// it assumes we merely want the file on the hard disk somewhere.
if(!KIO::NetAccess::file_copy(path, KUrl::fromPath(coverData->path))) {
kError() << "Failed to download cover from " << path << endl;
return NoMatch;
KIO::FileCopyJob *job = KIO::file_copy(
path, KUrl::fromPath(coverData->path),
-1 /* perms */,KIO::HideProgressInfo | KIO::Overwrite
);
QObject::connect(job, SIGNAL(result(KJob*)),
data()->coverProxy(), SLOT(handleResult(KJob*)));
data()->downloadJobs.insert(job, id);
job->start();
data()->requestSave(); // Save changes when possible.
return id;
}
/**
* This is called when our cover downloader has completed. Typically there
* should be no issues so we just need to ensure that the newly downloaded
* cover is picked up by invalidating any cache entry for it. If it didn't
* download successfully we're in kind of a pickle as we've already assigned
* a coverKey, which we need to go and erase.
*/
void CoverManager::jobComplete(KJob *job, bool completedSatisfactory)
{
coverKey id = NoMatch;
if(data()->downloadJobs.contains(job))
id = data()->downloadJobs[job];
if(id == NoMatch) {
kError() << "No information on what download job" << job << "is.";
data()->downloadJobs.remove(job);
return;
}
coverData->artist = artist.toLower();
coverData->album = album.toLower();
coverData->refCount = 0;
if(!completedSatisfactory) {
kError() << "Job" << job << "failed, but not handled yet.";
removeCover(id);
data()->downloadJobs.remove(job);
JuK::JuKInstance()->coverDownloaded(QPixmap());
return;
}
data()->covers[id] = coverData;
CoverDataPtr coverData = data()->covers[id];
// Make sure the new cover isn't inadvertently cached.
QPixmapCache::remove(QString("f%1").arg(coverData->path));
QPixmapCache::remove(QString("t%1").arg(coverData->path));
data()->requestSave(); // Save changes when possible.
return id;
JuK::JuKInstance()->coverDownloaded(coverFromData(*coverData, CoverManager::Thumbnail));
}
bool CoverManager::hasCover(coverKey id)
......
......@@ -23,8 +23,10 @@
#include <QString>
class CoverManagerPrivate;
class CoverProxy;
class QPixmap;
class QTimer;
class KJob;
template<class Key, class Value>
class QMap;
......@@ -278,6 +280,10 @@ public:
static const coverKey NoMatch;
private:
friend class CoverProxy; // Our QObject-wielding friend.
/// Called by CoverProxy to notify of a completed job.
static void jobComplete(KJob *job, bool completedSatisfactory);
static CoverManagerPrivate *data();
static QPixmap createThumbnail(const QPixmap &base);
};
......
/***************************************************************************
begin : Sun Mar 01 2009
copyright : (C) 2009 by Michael Pyne
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "coverproxy.h"
#include "covermanager.h"
#include <kdebug.h>
#include <kio/job.h>
CoverProxy::CoverProxy(QObject *parent) :
QObject(parent)
{
}
void CoverProxy::handleResult(KJob *job)
{
if(job->error()) {
kError() << "Cover download job failed with the following error:" << job->errorString();
CoverManager::jobComplete(job, false);
}
else {
CoverManager::jobComplete(job, true);
}
}
#include "coverproxy.moc"
/***************************************************************************
begin : Sun Mar 01 2009
copyright : (C) 2009 by Michael Pyne
email : michael.pyne@kdemail.net
***************************************************************************/
/***************************************************************************
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef JUK_COVERPROXY_H
#define JUK_COVERPROXY_H
#include <QtCore/QObject>
class KJob;
/**
* This class is responsible for tracking status of KIO::Jobs that are
* downloading covers for the CoverManager.
*/
class CoverProxy : public QObject
{
Q_OBJECT
public:
CoverProxy(QObject *parent = 0);
private slots:
void handleResult(KJob *);
};
#endif
......@@ -35,6 +35,7 @@
#include <kapplication.h>
#include <kglobalaccel.h>
#include <ktoolbarpopupaction.h>
#include <knotification.h>
#include <kdeversion.h>
#include <QCoreApplication>
......@@ -117,6 +118,21 @@ JuK* JuK::JuKInstance()
return m_instance;
}
void JuK::coverDownloaded(const QPixmap &cover)
{
QString event(cover.isNull() ? "coverFailed" : "coverDownloaded");
KNotification *notification = new KNotification(event, this);
notification->setPixmap(cover);
notification->setFlags(KNotification::CloseOnTimeout);
if(cover.isNull())
notification->setText(i18n("Your album art failed to download."));
else
notification->setText(i18n("Your album art has finished downloading."));
notification->sendEvent();
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
......
......@@ -42,6 +42,9 @@ public:
static JuK* JuKInstance();
// Use a null cover for failure
void coverDownloaded(const QPixmap &cover);
private:
void setupLayout();
void setupActions();
......
......@@ -18,7 +18,6 @@
#include "tag.h"
#include <kapplication.h>
#include <kio/netaccess.h>
#include <kio/job.h>
#include <klocale.h>
#include <kdebug.h>
......@@ -248,6 +247,7 @@ void WebCoverIconViewItem::imageResult(KJob *job)
QPainter p;
QRect targetRect(QPoint(0, 0), iconImage.size());
p.begin(&realImage);
p.setCompositionMode(QPainter::CompositionMode_Source);
// Center thumbnail in 80x80 pixmap
targetRect.setWidth(iconImage.width());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment