Skip to content

Commit

Permalink
cache audio tags
Browse files Browse the repository at this point in the history
  • Loading branch information
jsmolka committed May 27, 2018
1 parent b411187 commit e307719
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 44 deletions.
6 changes: 3 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@ A Groove Music like music player.

## Changes since lastest release
- add resource collection
- add tag caching
- fix library hovering
- improve song transition
- improve start time
- improve load time
- improve cache performance
- improve song transition

## Things to do

### Internal
- move library into music library
- use a member query
- split utils but keep header
- cache all audio information
- use QFileSystemWatcher

### Cosmetic
Expand Down
28 changes: 28 additions & 0 deletions src/core/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,34 @@ Audio::Audio(const QString &path) :
m_title = FileUtil::fileName(m_path);
}

/*
* Constructor. Used to read audios from the sql database.
*
* :param path: path
* :param title: title
* :param artist: artist
* :param album: album
* :param genre: genre
* :param year: year
* :param length: length
* :param coverId: cover id
*/
Audio::Audio(const QString &path, const QString &title, const QString &artist, const QString &album,
const QString &genre, int year, int track, int length, int coverId) :
m_valid(true),
m_path(path),
m_title(title),
m_artist(artist),
m_album(album),
m_genre(genre),
m_year(year),
m_track(track),
m_length(length),
m_coverId(coverId)
{

}

/*
* Destructor.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/core/audio.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class Audio
{
public:
Audio(const QString &path);
Audio(const QString &path, const QString &title, const QString &artist, const QString &album,
const QString &genre, int year, int track, int length, int coverId);
~Audio();

bool isValid() const;
Expand Down
121 changes: 105 additions & 16 deletions src/core/cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
*/
Cache::Cache()
{
if (QSqlDatabase::contains(dbName()))
return;

QSqlDatabase::addDatabase("QSQLITE", dbName());
if (!QSqlDatabase::contains(dbName()))
{
QSqlDatabase::addDatabase("QSQLITE", dbName());

createCovers();
createAudios();
createCovers();
createAudios();
}
}

/*
Expand All @@ -24,16 +24,89 @@ Cache::~Cache()
}

/*
* Inserts audio into cache. It adds the cover into the covers table and the
* path with a cover id into the audios table. No tags are stored inside the
* cache because TagLib is fast enough to reload tags at every startup.
* Also sets the cover id for later use.
* Loads an audio file from the cache. If it does not exist a nullptr will be
* returned instead.
*
* :param path: path
* :return: audio, nullptr at failure
*/
Audio * Cache::load(const QString &path)
{
Audio *audio = nullptr;

QSqlQuery query(db());
query.prepare("SELECT * FROM audios WHERE path = :path");
query.bindValue(":path", path);

if (!query.exec())
handleError(query);

if (query.first())
{
audio = new Audio(
query.value(0).toString(),
query.value(1).toString(),
query.value(2).toString(),
query.value(3).toString(),
query.value(4).toString(),
query.value(5).toInt(),
query.value(6).toInt(),
query.value(7).toInt(),
query.value(8).toInt()
);
}
return audio;
}

/*
* Inserts audio tags into the cache.
*
* :param audio: audio
* :return: success
*/
bool Cache::insertAudio(Audio *audio)
{
QSqlQuery query(db());
query.prepare(
"INSERT INTO audios VALUES ("
" :path,"
" :title,"
" :artist,"
" :album,"
" :genre,"
" :year,"
" :track,"
" :length,"
" :coverid"
")"
);
query.bindValue(":path", audio->path());
query.bindValue(":title", audio->title());
query.bindValue(":artist", audio->artist());
query.bindValue(":album", audio->album());
query.bindValue(":genre", audio->genre());
query.bindValue(":year", audio->year());
query.bindValue(":track", audio->track());
query.bindValue(":length", audio->length(false));
query.bindValue(":coverid", audio->coverId());

if (!query.exec())
{
handleError(query);
return false;
}
return true;
}

/*
* Inserts an audio cover into the cache. This assumes that the audio already
* has an entry in the audio table.
*
* :param audio: audio
* :param size: cover size, default 200
* :return: success
*/
bool Cache::insert(Audio *audio, int size)
bool Cache::insertCover(Audio *audio, int size)
{
int id = getOrInsertCover(audio->cover(size));
if (id == -1)
Expand All @@ -42,7 +115,11 @@ bool Cache::insert(Audio *audio, int size)
audio->setCoverId(id);

QSqlQuery query(db());
query.prepare("INSERT INTO audios VALUES (:path, :coverid)");
query.prepare(
"UPDATE audios "
"SET coverid = :coverid "
"WHERE path = :path"
);
query.bindValue(":path", audio->path());
query.bindValue(":coverid", id);

Expand All @@ -55,7 +132,9 @@ bool Cache::insert(Audio *audio, int size)
}

/*
* Checks if database contains audio. Also sets the cover id for later use.
* Checks if database contains audio. Also sets the cover id for later use. If
* an audio object has a valid cover id it definitely exists already because it
* only gets set inside the cache.
*
* :param audio: audio
* :return: contains
Expand All @@ -78,7 +157,11 @@ bool Cache::contains(Audio *audio)
if (!query.first())
return false;

audio->setCoverId(query.value(0).toInt());
int id = query.value(0).toInt();
if (id == -1)
return false;

audio->setCoverId(id);

return true;
}
Expand Down Expand Up @@ -182,8 +265,14 @@ void Cache::createAudios()
QString createAudios =
"CREATE TABLE IF NOT EXISTS audios("
" path TEXT PRIMARY KEY,"
" coverid INTEGER,"
" FOREIGN KEY (coverid) REFERENCES covers(id)"
" title TEXT,"
" artist TEXT,"
" album TEXT,"
" genre TEXT,"
" year INTEGER,"
" track INTEGER,"
" length INTEGER,"
" coverid INTEGER"
")";

QSqlQuery query(db());
Expand Down
6 changes: 5 additions & 1 deletion src/core/cache.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ class Cache
Cache();
~Cache();

bool insert(Audio *audio, int size = 200);
Audio * load(const QString &path);

bool insertAudio(Audio *audio);
bool insertCover(Audio *audio, int size = 200);

bool contains(Audio *audio);
QPixmap cover(Audio *audio, int size = 200);

Expand Down
27 changes: 10 additions & 17 deletions src/core/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
Library::Library(QObject *parent) :
QObject(parent),
m_sorted(false),
pm_audioPool(new ThreadPool(this)),
pm_cachePool(new ThreadPool(this))
pm_audioLoader(new AudioLoader(this)),
pm_cacheBuilder(new CacheBuilder(this))
{
connect(pm_audioPool, SIGNAL(finished()), this, SLOT(onAudioPoolFinished()));
connect(pm_audioPool, SIGNAL(finished()), this, SIGNAL(loaded()));
connect(pm_audioLoader, SIGNAL(loaded(Audio *)), this, SLOT(insert(Audio *)));
connect(pm_audioLoader, SIGNAL(finished()), this, SLOT(onAudioLoaderFinished()));
connect(pm_audioLoader, SIGNAL(finished()), this, SIGNAL(loaded()));
}

/*
Expand Down Expand Up @@ -89,14 +90,8 @@ Audios Library::audios() const
*/
void Library::load(const StringList &paths)
{
QVector<StringList> chunks = Util::chunk(uniqueFiles(paths), pm_audioPool->advised());
for (const StringList &chunk : chunks)
{
AudioLoader *loader = new AudioLoader(chunk);
connect(loader, SIGNAL(loaded(Audio *)), this, SLOT(insert(Audio *)));
pm_audioPool->add(loader);
}
pm_audioPool->start();
pm_audioLoader->setFiles(uniqueFiles(paths));
pm_audioLoader->start();
}

/*
Expand All @@ -107,21 +102,19 @@ void Library::load(const StringList &paths)
*/
void Library::insert(Audio *audio)
{
m_mutex.lock();
if (m_sorted)
insertBinary(audio);
else
append(audio);
m_mutex.unlock();
}

/*
* Gets called when the library is loaded and creates the cache builder.
*/
void Library::onAudioPoolFinished()
void Library::onAudioLoaderFinished()
{
pm_cachePool->add(new CacheBuilder(m_audios));
pm_cachePool->start();
pm_cacheBuilder->setAudios(m_audios);
pm_cacheBuilder->start();
}

/*
Expand Down
6 changes: 3 additions & 3 deletions src/core/library.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public slots:
void inserted(Audio *, int);

private slots:
void onAudioPoolFinished();
void onAudioLoaderFinished();

private:
int lowerBound(Audio *audio);
Expand All @@ -50,8 +50,8 @@ private slots:

bool m_sorted;
Audios m_audios;
ThreadPool *pm_audioPool;
ThreadPool *pm_cachePool;
AudioLoader *pm_audioLoader;
CacheBuilder *pm_cacheBuilder;
QMutex m_mutex;
QSet<QString> m_paths;

Expand Down
5 changes: 3 additions & 2 deletions src/core/threading/abstractthread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ bool AbstractThread::isAbort() const
void AbstractThread::abort()
{
m_abort = true;
quit();
wait();

if (!wait(5000))
Logger::log("AbstractThread: Could not abort within 5 seconds");
}
2 changes: 2 additions & 0 deletions src/core/threading/abstractthread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

#include <QThread>

#include "logger.hpp"

class AbstractThread : public QThread
{
Q_OBJECT
Expand Down
9 changes: 8 additions & 1 deletion src/core/threading/audioloader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,19 @@ void AudioLoader::setFiles(const StringList &files)
*/
void AudioLoader::run()
{
Cache cache;
for (const QString &file : m_files)
{
if (isAbort())
return;

Audio *audio = new Audio(file);
Audio *audio = cache.load(file);
if (!audio)
{
audio = new Audio(file);
cache.insertAudio(audio);
}

if (audio->isValid())
emit loaded(audio);
else
Expand Down
1 change: 1 addition & 0 deletions src/core/threading/audioloader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "abstractthread.hpp"
#include "audio.hpp"
#include "cache.hpp"
#include "logger.hpp"
#include "types.hpp"
#include "utils.hpp"
Expand Down
6 changes: 5 additions & 1 deletion src/core/threading/cachebuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,19 @@ void CacheBuilder::setAudios(const Audios &audios)
/*
* Loads audio covers.
*/
#include <QDebug>
void CacheBuilder::run()
{
Cache cache;
for (Audio *audio : m_audios)
{
if (isAbort())
{
qDebug() << "aborted :|";
return;
}

if (!cache.contains(audio))
cache.insert(audio);
cache.insertCover(audio);
}
}

0 comments on commit e307719

Please sign in to comment.