Commit 3a5a63d6 authored by Scott Wheeler's avatar Scott Wheeler

Added caching of tag data. This speeds up load time *tremendously* to

the point that reading the meta data, provided that is has been read
before is negligible.

svn path=/trunk/kdemultimedia/juk/; revision=183593
parent a8b7467a
......@@ -15,55 +15,82 @@
* *
***************************************************************************/
#include <kstandarddirs.h>
#include <kdebug.h>
#include "cache.h"
#include "cachedtag.h"
Cache *Cache::cache = 0;
////////////////////////////////////////////////////////////////////////////////
// Cache public methods
// public methods
////////////////////////////////////////////////////////////////////////////////
Cache *Cache::instance()
{
if(!cache)
if(cache == 0) {
cache = new Cache();
cache->load();
}
return(cache);
}
CacheItem *Cache::item(const QString &file) const
void Cache::load()
{
return(0);
}
QString cacheFileName = KGlobal::dirs()->saveLocation("appdata") + "cache";
////////////////////////////////////////////////////////////////////////////////
// Cache protected methods
////////////////////////////////////////////////////////////////////////////////
QFile f(cacheFileName);
Cache::Cache()
{
setAutoDelete(true);
if(!f.open(IO_ReadOnly))
return;
QDataStream s(&f);
while(!s.atEnd()) {
QString fileName;
s >> fileName;
CachedTag *t = new CachedTag(fileName);
s >> *t;
if(!t->current())
delete(t);
}
f.close();
}
Cache::~Cache()
void Cache::save()
{
delete(cache);
}
QString cacheFileName = KGlobal::dirs()->saveLocation("appdata") + "cache";
////////////////////////////////////////////////////////////////////////////////
// CacheItem public methods
////////////////////////////////////////////////////////////////////////////////
QFile f(cacheFileName);
CacheItem::CacheItem()
{
if(!f.open(IO_WriteOnly))
return;
QDataStream s(&f);
for(QDictIterator<Tag>it(*this); it.current(); ++it) {
s << it.current()->absFilePath()
<< *(it.current());
}
f.close();
}
CacheItem::CacheItem(const Tag &tag)
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////
Cache::Cache() : QDict<Tag>()
{
}
CacheItem::~CacheItem()
Cache::~Cache()
{
delete(cache);
}
......@@ -23,41 +23,19 @@
#include "tag.h"
// Eventually this should implement the same interface as the Tag class; in fact
// there should be an abstract API for both of them to use. But, for the
// moment this is just a place holder to fill in the design.
class CacheItem;
class Cache : public QDict<CacheItem>
class Cache : public QDict<Tag>
{
public:
class Item;
static Cache *instance();
CacheItem *item(const QString &fileName) const;
void save();
protected:
Cache();
virtual ~Cache();
void load();
private:
static Cache *cache;
};
class CacheItem
{
public:
CacheItem();
CacheItem(const Tag &tag);
virtual ~CacheItem();
QString track() const { return QString::null; }
QString artist() const { return QString::null; }
QString album() const { return QString::null; }
QString trackNumber() const { return QString::null; }
QString length() const { return QString::null; }
};
#endif
......@@ -16,104 +16,213 @@
***************************************************************************/
#include "cachedtag.h"
#include "cache.h"
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
CachedTag::CachedTag(const QString &file) : Tag(file)
{
tagYear = 0;
tagTrackNumber = 0;
tagSeconds = 0;
tagExists = false;
externalTag = 0;
}
CachedTag::~CachedTag()
{
delete(externalTag);
}
void CachedTag::save()
{
if(externalTag)
externalTag->save();
}
QString CachedTag::track() const
{
return(QString::null);
if(externalTag)
return(externalTag->track());
else
return(tagTrack);
}
QString CachedTag::artist() const
{
return(QString::null);
if(externalTag)
return(externalTag->artist());
else
return(tagArtist);
}
QString CachedTag::album() const
{
return(QString::null);
if(externalTag)
return(externalTag->album());
else
return(tagAlbum);
}
Genre CachedTag::genre() const
{
Genre g;
return(g);
if(externalTag)
return(externalTag->genre());
else
return(tagGenre);
}
int CachedTag::trackNumber() const
{
return(0);
if(externalTag)
return(externalTag->trackNumber());
else
return(tagTrackNumber);
}
QString CachedTag::trackNumberString() const
{
return(QString::null);
if(externalTag)
return(externalTag->trackNumberString());
else
return(tagTrackNumberString);
}
int CachedTag::year() const
{
return(0);
if(externalTag)
return(externalTag->year());
else
return(tagYear);
}
QString CachedTag::yearString() const
{
return(QString::null);
if(externalTag)
return(externalTag->yearString());
else
return(tagYearString);
}
QString CachedTag::comment() const
{
return(QString::null);
if(externalTag)
return(externalTag->comment());
else
return(tagComment);
}
bool CachedTag::hasTag() const
{
return(false);
if(externalTag)
return(externalTag->hasTag());
else
return(tagExists);
}
void CachedTag::setTrack(const QString &value)
{
proxiedTag()->setTrack(value);
}
void CachedTag::setArtist(const QString &value)
{
proxiedTag()->setArtist(value);
}
void CachedTag::setAlbum(const QString &value)
{
proxiedTag()->setAlbum(value);
}
void CachedTag::setGenre(const Genre &value)
{
proxiedTag()->setGenre(value);
}
void CachedTag::setTrackNumber(int value)
{
proxiedTag()->setTrackNumber(value);
}
void CachedTag::setYear(int value)
{
proxiedTag()->setYear(value);
}
void CachedTag::setComment(const QString &value)
{
proxiedTag()->setComment(value);
}
QString CachedTag::bitrateString() const
{
return(tagBitrateString);
}
QString CachedTag::lengthString() const
{
return(tagLengthString);
}
int CachedTag::seconds() const
{
return(tagSeconds);
}
bool CachedTag::current() const
{
return(fileExists() &&
modificationTime.isValid() &&
lastModified().isValid() &&
modificationTime >= Tag::lastModified());
}
QDataStream &CachedTag::read(QDataStream &s)
{
s >> int(tagExists)
>> tagTrack
>> tagArtist
>> tagAlbum
>> tagGenre
>> tagTrackNumber
>> tagTrackNumberString
>> tagYear
>> tagYearString
>> tagComment
>> tagBitrateString
>> tagLengthString
>> tagSeconds
>> fileName
>> modificationTime;
return(s);
}
////////////////////////////////////////////////////////////////////////////////
// private methods
////////////////////////////////////////////////////////////////////////////////
Tag *CachedTag::proxiedTag()
{
if(!externalTag) {
Cache::instance()->remove(absFilePath());
externalTag = Tag::createTag(absFilePath(), true);
}
return(externalTag);
}
////////////////////////////////////////////////////////////////////////////////
// related functions
////////////////////////////////////////////////////////////////////////////////
QDataStream &operator>>(QDataStream &s, CachedTag &t)
{
return t.read(s);
}
......@@ -18,6 +18,8 @@
#ifndef CACHEDTAG_H
#define CACHEDTAG_H
#include <qdatastream.h>
#include "tag.h"
class CachedTag : public Tag
......@@ -27,6 +29,7 @@ public:
virtual ~CachedTag();
virtual void save();
virtual bool hasTag() const;
virtual QString track() const;
virtual QString artist() const;
......@@ -37,7 +40,6 @@ public:
virtual int year() const;
virtual QString yearString() const;
virtual QString comment() const;
virtual bool hasTag() const;
virtual void setTrack(const QString &value);
virtual void setArtist(const QString &value);
......@@ -45,7 +47,44 @@ public:
virtual void setGenre(const Genre &value);
virtual void setTrackNumber(int value);
virtual void setYear(int value);
virtual void setComment(const QString &value);
virtual void setComment(const QString &value);
virtual QString bitrateString() const;
virtual QString lengthString() const;
virtual int seconds() const;
// CachedTag specific methods
/**
* Checks to see if the cache for this item is up to date.
*/
inline bool current() const;
QDataStream &read(QDataStream &s);
private:
Tag *proxiedTag();
Tag *externalTag;
QString tagTrack;
QString tagArtist;
QString tagAlbum;
Genre tagGenre;
int tagTrackNumber;
QString tagTrackNumberString;
int tagYear;
QString tagYearString;
QString tagComment;
QString tagBitrateString;
QString tagLengthString;
int tagSeconds;
bool tagExists;
QString fileName;
QDateTime modificationTime;
};
QDataStream &operator>>(QDataStream &s, CachedTag &t);
#endif
......@@ -21,6 +21,7 @@
#include "collectionlist.h"
#include "playlistsplitter.h"
#include "cache.h"
////////////////////////////////////////////////////////////////////////////////
// static methods
......@@ -36,6 +37,9 @@ CollectionList *CollectionList::instance()
void CollectionList::initialize(PlaylistSplitter *s, QWidget *parent)
{
list = new CollectionList(s, parent);
for(QDictIterator<Tag>it(*Cache::instance()); it.current(); ++it)
new CollectionListItem(it.current()->fileInfo());
}
////////////////////////////////////////////////////////////////////////////////
......
......@@ -27,6 +27,7 @@
#include "playlistsplitter.h"
#include "collectionlist.h"
#include "slideraction.h"
#include "cache.h"
////////////////////////////////////////////////////////////////////////////////
// public members
......@@ -135,6 +136,7 @@ void JuK::setupPlayer()
}
}
void JuK::processArgs()
{
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
......@@ -189,6 +191,7 @@ void JuK::saveConfig()
bool JuK::queryClose()
{
Cache::instance()->save();
saveConfig();
delete(splitter);
return(true);
......
......@@ -148,17 +148,17 @@ void OggTag::setComment(const QString &value)
QString OggTag::bitrateString() const
{
return(readBitrate(metaInfo));
return readBitrate(metaInfo);
}
QString OggTag::lengthString() const
{
return(readLength(metaInfo));
return readLength(metaInfo);
}
int OggTag::seconds() const
{
return(readSeconds(metaInfo));
return readSeconds(metaInfo);
}
////////////////////////////////////////////////////////////////////////////////
......
......@@ -21,7 +21,6 @@
#include "playlist.h"
#include "collectionlist.h"
////////////////////////////////////////////////////////////////////////////////
// PlaylistItem public methods
////////////////////////////////////////////////////////////////////////////////
......@@ -37,9 +36,9 @@ void PlaylistItem::setFile(const QString &file)
refresh();
}
Tag *PlaylistItem::getTag()
Tag *PlaylistItem::tag() const
{
return(data->getTag());
return(data->tag());
}
// QFileInfo-ish methods
......@@ -103,44 +102,16 @@ void PlaylistItem::setData(Data *d)
void PlaylistItem::refreshImpl()
{
// This should be the only function that needs to be rewritten if the structure of
// PlaylistItemData changes. Also, currently this also inserts things into the
// album and artist registries of the Playlist. If something like sorted insert
// happens at some point it could either be implemented here or in a subclass of
// QValueList. And another note: the artist/album registry doesn't remove items
// when they no longer exist in the list view. I decided that this is too much work
// for something not very useful at the moment, but at some point, a QValueList of
// a subclass of QPair could track such things...
// if the text has changed and the artist registry of the Playlist doens't contain
// this artist, add it to the mentioned registry
if(Cache::instance()->item(absFilePath())) {
// ... do stuff relative to a cache hit. At the moment this will never
// happen since the cache isn't yet implemented.
// The current though is that "Tag" and "Cache::Item" should share a
// common virtual interface so that the below code could actually be
// used through that interface. In this if block it would just be
// decided what object the meta data was going to come from and then
// something like metaData->getArtist() could be used, where metaData
// is either a Tag or a Cache::Item.
// And on further thought this should also be hidden in
// PlaylistItem::Data. I'll work on that later.
// PlaylistItemData changes.
}
else {
setText(TrackColumn, getTag()->track());
setText(ArtistColumn, getTag()->artist());
setText(AlbumColumn, getTag()->album());
setText(TrackNumberColumn, getTag()->trackNumberString());
setText(GenreColumn, getTag()->genre());
setText(YearColumn, getTag()->yearString());
setText(LengthColumn, getTag()->lengthString());
setText(FileNameColumn, filePath());
}
setText(TrackColumn, tag()->track());
setText(ArtistColumn, tag()->artist());
setText(AlbumColumn, tag()->album());
setText(TrackNumberColumn, tag()->trackNumberString());
setText(GenreColumn, tag()->genre());
setText(YearColumn, tag()->yearString());
setText(LengthColumn, tag()->lengthString());
setText(FileNameColumn, filePath());
}
////////////////////////////////////////////////////////////////////////////////
......@@ -169,23 +140,22 @@ int PlaylistItem::compare(QListViewItem *item, int column, bool ascending) const
// non-const pointer. Yuck.
PlaylistItem *playlistItem = dynamic_cast<PlaylistItem *>(item);
PlaylistItem *thisPlaylistItem = const_cast<PlaylistItem *>(this);
// The following statments first check to see if you can sort based on the
// specified column. If the values for the two PlaylistItems are the same
// in that column it then trys to sort based on columns 1, 2, 3 and 0,
// (artist, album, track number, track name) in that order.
if(playlistItem && thisPlaylistItem) {
if(compare(thisPlaylistItem, playlistItem, column, ascending) != 0)
return(compare(thisPlaylistItem, playlistItem, column, ascending));
if(playlistItem) {
if(compare(this, playlistItem, column, ascending) != 0)
return(compare(this, playlistItem, column, ascending));
else {
for(int i = ArtistColumn; i <= TrackNumberColumn; i++) {
if(compare(thisPlaylistItem, playlistItem, i, ascending) != 0)
return(compare(thisPlaylistItem, playlistItem, i, ascending));
if(compare(this, playlistItem, i, ascending) != 0)
return(compare(this, playlistItem, i, ascending));
}
if(compare(thisPlaylistItem, playlistItem, TrackColumn, ascending) != 0)
return(compare(thisPlaylistItem, playlistItem, TrackColumn, ascending));
if(compare(this, playlistItem, TrackColumn, ascending) != 0)
return(compare(this, playlistItem, TrackColumn, ascending));
return(0);
}
}
......@@ -193,20 +163,20 @@ int PlaylistItem::compare(QListViewItem *item, int column, bool ascending) const
return(0); // cast failed, something is wrong
}
int PlaylistItem::compare(PlaylistItem *firstItem, PlaylistItem *secondItem, int column, bool ascending) const
int PlaylistItem::compare(const PlaylistItem *firstItem, const PlaylistItem *secondItem, int column, bool ascending) const
{
if(column == TrackNumberColumn) {
if(firstItem->getTag()->trackNumber() > secondItem->getTag()->trackNumber())
if(firstItem->tag()->trackNumber() > secondItem->tag()->trackNumber())
return(1);
else if(firstItem->getTag()->trackNumber() < secondItem->getTag()->trackNumber())
else if(firstItem->tag()->trackNumber() < secondItem->tag()->trackNumber())
return(-1);
else
return(0);
}
else if(column == LengthColumn) {
if(firstItem->getTag()->seconds() > secondItem->getTag()->seconds())
if(firstItem->tag()->seconds() > secondItem->tag()->seconds())
return(1);
else if(firstItem->getTag()->seconds() < secondItem->getTag()->seconds())
else if(firstItem->tag()->seconds() < secondItem->tag()->seconds())
return(-1);
else
return(0);
......@@ -221,7 +191,7 @@ int PlaylistItem::compare(PlaylistItem *firstItem, PlaylistItem *secondItem, int
PlaylistItem::Data *PlaylistItem::Data::newUser(const QFileInfo &file)
{
return(new Data(file));
return new Data(file);
}
PlaylistItem::Data *PlaylistItem::Data::newUser()
......@@ -232,11 +202,8 @@ PlaylistItem::Data *PlaylistItem::Data::newUser()
void PlaylistItem::Data::refresh()
{
delete(cache);
delete(tag);
cache = 0;
tag = 0;
delete(dataTag);
dataTag = Tag::createTag(filePath());
}
void PlaylistItem::Data::deleteUser()
......@@ -248,17 +215,15 @@ void PlaylistItem::Data::deleteUser()
delete(this);
}
Tag *PlaylistItem::Data::getTag()
Tag *PlaylistItem::Data::tag() const
{
if(!tag)
tag = Tag::createTag(filePath());
return(tag);
return(dataTag);
}
void PlaylistItem::Data::setFile(const QString &file)
{
delete(tag);
tag = 0;
delete(dataTag);
dataTag = 0;
QFileInfo::setFile(file);
}
......@@ -270,22 +235,12 @@ void PlaylistItem::Data::setFile(const QString &file)
PlaylistItem::Data::Data(const QFileInfo &file) : QFileInfo(file)
{
referenceCount = 1;
// initialize pointers to null
cache = 0;
tag = 0;
dataTag = Tag::createTag(filePath());
}
PlaylistItem::Data::~Data()
{
// Create our cache "on the way out" to avoid having lots of duplicate copies
// of the same information in memory. This checks to see if the item is in
// the cache and
if(tag && !cache && !Cache::instance()->isEmpty() && !Cache::instance()->find(absFilePath()) )