initial commit

This commit is contained in:
Blue 2018-08-05 00:46:25 +03:00 committed by Юрий Губич
commit 4b60ece582
327 changed files with 28286 additions and 0 deletions

40
corax/CMakeLists.txt Normal file
View file

@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 2.8.12)
project(corax)
find_package(Qt5Core REQUIRED)
find_package(Qt5Network REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(HEADERS
corax.h
tools/parser.h
tools/audioid.h
tools/audiotag.h
)
set(SOURCES
main.cpp
corax.cpp
tools/parser.cpp
tools/audioid.cpp
tools/audiotag.cpp
)
add_executable(corax ${HEADERS} ${SOURCES})
target_link_libraries(corax Qt5::Core)
target_link_libraries(corax Qt5::Network)
target_link_libraries(corax wSocket)
target_link_libraries(corax wDispatcher)
target_link_libraries(corax utils)
target_link_libraries(corax wModel)
target_link_libraries(corax wController)
target_link_libraries(corax wServerUtils)
target_link_libraries(corax wDatabase)
target_link_libraries(corax tag)
target_link_libraries(corax tools)
install(TARGETS corax RUNTIME DESTINATION bin)

236
corax/corax.cpp Normal file
View file

@ -0,0 +1,236 @@
#include "corax.h"
#include <iostream>
using std::cout;
using std::endl;
Corax* Corax::corax = 0;
Corax::Corax(QObject *parent):
QObject(parent),
server(new W::Server(W::String(u"Corax"), this)),
logger(new W::Logger()),
parentReporter(new W::ParentReporter()),
attributes(new M::Attributes(W::Address({u"attributes"}))),
commands(new U::Commands(W::Address{u"management"})),
connector(0),
dispatcher(new W::Dispatcher()),
caches(),
parsers()
{
if (corax != 0)
{
throw SingletonError();
}
Corax::corax = this;
connector = new U::Connector(dispatcher, server, commands);
connector->addIgnoredNode(W::String(u"Lorgar"));
connector->addIgnoredNode(W::String(u"Roboute"));
connect(attributes, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(commands, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(connector, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(connector, SIGNAL(nodeConnected(const W::String&)), SLOT(onNodeConnected(const W::String&)));
connect(connector, SIGNAL(nodeDisconnected(const W::String&)), SLOT(onNodeDisconnected(const W::String&)));
connect(server, SIGNAL(connectionCountChange(uint64_t)), SLOT(onConnectionCountChanged(uint64_t)));
dispatcher->registerDefaultHandler(parentReporter);
dispatcher->registerDefaultHandler(logger);
attributes->addAttribute(W::String(u"connectionsCount"), new M::String(W::String(u"0"), W::Address({u"attributes", u"connectionCount"})));
attributes->addAttribute(W::String(u"name"), new M::String(W::String(u"Corax"), W::Address({u"attributes", u"name"})));
attributes->addAttribute(W::String(u"version"), new M::String(W::String(u"0.0.2"), W::Address({u"attributes", u"version"})));
createCaches();
createHandlers();
}
Corax::~Corax()
{
std::map<W::String, Parser*>::iterator pbeg = parsers.begin();
std::map<W::String, Parser*>::iterator pend = parsers.end();
for (; pbeg != pend; ++pbeg) {
delete pbeg->second;
}
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::iterator end = caches.end();
for (; beg != end; ++beg) {
delete beg->second;
}
delete connector;
dispatcher->unregisterDefaultHandler(logger);
delete commands;
delete attributes;
delete logger;
delete dispatcher;
Corax::corax = 0;
}
void Corax::onConnectionCountChanged(uint64_t count)
{
attributes->setAttribute(W::String(u"connectionsCount"), new W::String(std::to_string(count)));
}
void Corax::start()
{
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::iterator end = caches.end();
cout << "Starting corax..." << endl;
server->listen(8080);
cout << "Registering models..." << endl;
attributes->registerModel(dispatcher, server);
commands->registerModel(dispatcher, server);
for (; beg != end; ++beg) {
beg->second->registerModel(dispatcher, server);
}
cout << "Opening caches..." << endl;
beg = caches.begin();
for (; beg != end; ++beg) {
beg->second->open();
}
commands->enableCommand(W::String(u"clearCache"), true);
cout << "Corax is ready" << endl;
}
void Corax::stop()
{
std::map<W::String, ResourceCache*>::iterator beg = caches.begin();
std::map<W::String, ResourceCache*>::iterator end = caches.end();
cout << "Stopping corax..." << endl;
commands->unregisterModel();
attributes->unregisterModel();
for (; beg != end; ++beg) {
beg->second->unregisterModel();
}
server->stop();
}
void Corax::onModelServiceMessage(const QString& msg)
{
cout << msg.toStdString() << endl;
}
void Corax::addCache(ResourceCache* cache)
{
attributes->addAttribute(cache->name, new M::String(W::String(u"0"), W::Address({u"attributes", cache->name})));
connect(cache, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(cache, SIGNAL(countChange(uint64_t)), SLOT(onCacheCountChange(uint64_t)));
parentReporter->registerParent(cache->getAddress(), cache->subscribeMember);
caches.insert(std::make_pair(cache->name, cache));
}
void Corax::h_clearCache(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::String& name = static_cast<const W::String&>(vc.at(u"name"));
cout << "received command to clear cache " << name.toString() << endl;
std::map<W::String, ResourceCache*>::iterator itr = caches.find(name);
if (itr == caches.end()) {
cout << "cache " << name.toString() << " doesn't exist" << endl;
} else {
itr->second->clear();
}
}
void Corax::h_parseDirectory(const W::Event& ev)
{
const W::Vocabulary& vc = static_cast<const W::Vocabulary&>(ev.getData());
const W::String& path = static_cast<const W::String&>(vc.at(u"path"));
cout << "received command to parse directory " << path.toString() << endl;
std::map<W::String, Parser*>::const_iterator itr = parsers.find(path);
if (itr != parsers.end()) {
cout << "directory " << path.toString() << " is already being parsed" << endl;
} else {
const W::Socket& socket = connector->getNodeSocket(W::String(u"Perturabo"));
ResourceCache* music = caches.at(W::String(u"music"));
ResourceCache* images = caches.at(W::String(u"images"));
Parser* parser = new Parser(&socket, dispatcher, music, images);
parsers.insert(std::make_pair(path, parser));
connect(parser, SIGNAL(serviceMessage(const QString&)), SLOT(onModelServiceMessage(const QString&)));
connect(parser, SIGNAL(done(const W::String&)), SLOT(onParserDone(const W::String&)));
parser->parse(path);
}
}
void Corax::createCaches()
{
ResourceCache* music = new ResourceCache(W::String(u"music"));
ResourceCache* images = new ResourceCache(W::String(u"images"));
addCache(music);
addCache(images);
}
void Corax::createHandlers()
{
W::Handler* clearCache = W::Handler::create(W::Address({u"management", u"clearCache"}), this, &Corax::_h_clearCache);
W::Vocabulary clearArgs;
clearArgs.insert(u"name", W::Uint64(W::Object::string));
commands->addCommand(W::String(u"clearCache"), clearCache, clearArgs);
W::Handler* parseDirectory = W::Handler::create(W::Address({u"management", u"parseDirectory"}), this, &Corax::_h_parseDirectory);
W::Vocabulary parseArgs;
parseArgs.insert(u"path", W::Uint64(W::Object::string));
commands->addCommand(W::String(u"parseDirectory"), parseDirectory, parseArgs);
}
void Corax::onParserDone(const W::String& path)
{
std::map<W::String, Parser*>::const_iterator itr = parsers.find(path);
delete itr->second;
parsers.erase(itr);
}
void Corax::onCacheCountChange(uint64_t count)
{
ResourceCache* cache = static_cast<ResourceCache*>(sender());
attributes->setAttribute(cache->name, W::String(std::to_string(count)));
}
void Corax::onNodeConnected(const W::String& name)
{
cout << "connected node " << name.toString() << endl;
if (name == u"Perturabo") {
commands->enableCommand(W::String(u"parseDirectory"), true);
}
}
void Corax::onNodeDisconnected(const W::String& name)
{
cout << "disconnected node " << name.toString() << endl;
if (name == u"Perturabo") {
commands->enableCommand(W::String(u"parseDirectory"), false);
}
}

88
corax/corax.h Normal file
View file

@ -0,0 +1,88 @@
#ifndef CORAX_H
#define CORAX_H
#include <map>
#include <QtCore/QObject>
#include <QtCore/QString>
#include <wSocket/socket.h>
#include <wSocket/server.h>
#include <wType/string.h>
#include <wType/uint64.h>
#include <wType/event.h>
#include <wType/address.h>
#include <wType/vocabulary.h>
#include <wDispatcher/dispatcher.h>
#include <wDispatcher/logger.h>
#include <wDispatcher/parentreporter.h>
#include <wModel/modelstring.h>
#include <wModel/attributes.h>
#include <utils/exception.h>
#include <wServerUtils/commands.h>
#include <wServerUtils/connector.h>
#include <wDatabase/resourcecache.h>
#include "tools/parser.h"
class Corax: public QObject
{
Q_OBJECT
public:
Corax(QObject *parent = 0);
~Corax();
static Corax* corax;
private:
W::Server *server;
W::Logger *logger;
W::ParentReporter* parentReporter;
M::Attributes* attributes;
U::Commands* commands;
U::Connector* connector;
W::Dispatcher *dispatcher;
std::map<W::String, ResourceCache*> caches;
std::map<W::String, Parser*> parsers;
handler(clearCache);
handler(parseDirectory);
public slots:
void start();
void stop();
private slots:
void onModelServiceMessage(const QString& msg);
void onConnectionCountChanged(uint64_t count);
void onParserDone(const W::String& path);
void onCacheCountChange(uint64_t count);
void onNodeConnected(const W::String& name);
void onNodeDisconnected(const W::String& name);
private:
void addCache(ResourceCache* cache);
void createCaches();
void createHandlers();
private:
class SingletonError:
public Utils::Exception
{
public:
SingletonError():Exception(){}
std::string getMessage() const{return "Corax is a singleton, there was an attempt to construct it at the second time";}
};
};
#endif // CORAX_H

18
corax/main.cpp Normal file
View file

@ -0,0 +1,18 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QTimer>
#include <utils/signalcatcher.h>
#include "corax.h"
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
W::SignalCatcher sc(&app);
Corax* corax = new Corax(&app);
QTimer::singleShot(0, corax, SLOT(start()));
QObject::connect(&app, SIGNAL(aboutToQuit()), corax, SLOT(stop()));
return app.exec();
}

61
corax/tools/audioid.cpp Normal file
View file

@ -0,0 +1,61 @@
#include "audioid.h"
AudioId::AudioId(const W::String& p_artist, const W::String& p_album, const W::String& p_name):
artist(p_artist),
album(p_album),
name(p_name)
{
}
AudioId::AudioId(const AudioId& other):
artist(other.artist),
album(other.album),
name(other.name)
{
}
bool AudioId::operator==(const AudioId& other) const
{
return name == other.name && album == other.album && artist == other.artist;
}
bool AudioId::operator!=(const AudioId& other) const
{
return operator==(other);
}
bool AudioId::operator>(const AudioId& other) const
{
if (name == other.name) {
if (album == other.album) {
return name > other.name;
} else {
return album > other.album;
}
} else {
return name > other.name;
}
}
bool AudioId::operator<(const AudioId& other) const
{
if (name == other.name) {
if (album == other.album) {
return name < other.name;
} else {
return album < other.album;
}
} else {
return name < other.name;
}
}
bool AudioId::operator>=(const AudioId& other) const
{
return !operator<(other);
}
bool AudioId::operator<=(const AudioId& other) const
{
return !operator>(other);
}

28
corax/tools/audioid.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef AUDIOID_H
#define AUDIOID_H
/**
* @todo write docs
*/
#include <wType/string.h>
class AudioId
{
public:
AudioId(const W::String& p_artist, const W::String& p_album, const W::String& p_name);
AudioId(const AudioId& other);
bool operator==(const AudioId& other) const;
bool operator!=(const AudioId& other) const;
bool operator<(const AudioId& other) const;
bool operator>(const AudioId& other) const;
bool operator<=(const AudioId& other) const;
bool operator>=(const AudioId& other) const;
const W::String artist;
const W::String album;
const W::String name;
};
#endif // AUDIOID_H

35
corax/tools/audiotag.cpp Normal file
View file

@ -0,0 +1,35 @@
#include "audiotag.h"
AudioTag::AudioTag(const AudioTag& other):
fileRef(other.fileRef)
{
}
AudioTag::AudioTag(const T::File& file):
fileRef(file.getPath().toString().c_str())
{
}
AudioTag::~AudioTag()
{
}
W::String AudioTag::getTitle() const
{
return W::String(fileRef.tag()->title().to8Bit(true));
}
W::String AudioTag::getAlbum() const
{
return W::String(fileRef.tag()->album().to8Bit(true));
}
W::String AudioTag::getArtist() const
{
return W::String(fileRef.tag()->artist().to8Bit(true));
}
W::Uint64 AudioTag::getYear() const
{
return W::Uint64(fileRef.tag()->year());
}

29
corax/tools/audiotag.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef AUDIOTAG_H
#define AUDIOTAG_H
#include <wType/string.h>
#include <wType/uint64.h>
#include <tools/file.h>
#include <taglib/fileref.h>
#include <taglib/tag.h>
class AudioTag
{
public:
AudioTag(const T::File& file);
AudioTag(const AudioTag& other);
~AudioTag();
W::String getTitle() const;
W::String getAlbum() const;
W::String getArtist() const;
W::Uint64 getYear() const;
private:
TagLib::FileRef fileRef;
};
#endif // AUDIOTAG_H

309
corax/tools/parser.cpp Normal file
View file

@ -0,0 +1,309 @@
#include "parser.h"
Parser::Parser(const W::Socket* p_socket, W::Dispatcher* p_dp, ResourceCache* p_audio, ResourceCache* p_images):
QObject(),
socket(p_socket),
dp(p_dp),
songs(W::Address({u"songs"})),
albums(W::Address({u"albums"})),
artists(W::Address({u"artists"})),
audio(p_audio),
images(p_images),
path(),
songsReady(false),
albumsReady(false),
artistsReady(false),
state(idle),
foundImages(),
foundAudios()
{
connect(&songs, SIGNAL(ready()), this, SLOT(onSongsReady()));
connect(&songs, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
connect(&albums, SIGNAL(ready()), this, SLOT(onAlbumsReady()));
connect(&albums, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
connect(&artists, SIGNAL(ready()), this, SLOT(onArtistsReady()));
connect(&artists, SIGNAL(serviceMessage(const QString&)), this, SIGNAL(serviceMessage(const QString&)));
songs.registerController(dp, socket);
albums.registerController(dp, socket);
artists.registerController(dp, socket);
}
Parser::~Parser()
{
}
void Parser::onSongsReady()
{
songsReady = true;
emit serviceMessage("Songs are ready");
checkState();
}
void Parser::onAlbumsReady()
{
albumsReady = true;
emit serviceMessage("Albums are ready");
checkState();
}
void Parser::onArtistsReady()
{
artistsReady = true;
emit serviceMessage("Artists are ready");
checkState();
}
void Parser::checkState()
{
switch (state) {
case idle:
break;
case waitingForCollections:
if (songsReady && albumsReady && artistsReady) {
state = parsingDirectory;
parseDirectory();
}
break;
case parsingDirectory:
parseDirectory();
break;
case updatingMusicDataBase:
if (songsReady && albumsReady && artistsReady) {
updateMusicDataBase();
}
break;
case updatingImageDataBase:
if (songsReady && albumsReady && artistsReady) {
updateImageDataBase();
}
break;
}
}
void Parser::parse(const W::String& p_path)
{
if (state != idle) {
emit serviceMessage("An attempt to make parsing while another isn't finished, quitting");
throw 15;
}
path = p_path;
if (!songs.isSubscribed()) {
songs.subscribe();
}
if (!albums.isSubscribed()) {
albums.subscribe();
}
if (!artists.isSubscribed()) {
artists.subscribe();
}
if (!songsReady || !albumsReady || !artistsReady) {
state = waitingForCollections;
} else {
state = parsingDirectory;
}
checkState();
}
void Parser::parseDirectory()
{
emit serviceMessage(QString("Starting to parse directory ") + path.toString().c_str());
std::list<T::File> *list = new std::list<T::File>();
bool success = T::File::readDirectoryRecursive(path, list);
if (success) {
emit serviceMessage("Successully recursively red the directory");
std::set<uint64_t> presentMusicId = audio->getAllIdentificators();
std::set<W::String> presentAudio;
std::set<uint64_t>::const_iterator pai(presentMusicId.begin()), pae(presentMusicId.end());
for (; pai != pae; ++pai) {
presentAudio.insert(audio->getPath(*pai));
}
std::set<uint64_t> presentImageId = images->getAllIdentificators();
std::set<W::String> presentImages;
std::set<uint64_t>::const_iterator pii(presentImageId.begin()), pie(presentImageId.end());
for (; pii != pie; ++pii) {
presentImages.insert(images->getPath(*pii));
}
std::list<T::File>::const_iterator itr = list->begin();
std::list<T::File>::const_iterator end = list->end();
for (; itr != end; ++itr) {
W::String path = itr->getPath();
emit serviceMessage(QString("Analysing ") + path.toString().c_str());
if (itr->suffix() == u"mp3") {
if (presentAudio.find(itr->getPath()) == presentAudio.end()) {
AudioTag tag(*itr);
uint64_t id = audio->addResource(itr->getPath());
AudioId aid(tag.getArtist(), tag.getAlbum(), tag.getTitle());
foundAudios.insert(std::make_pair(aid, id));
}
} else if (itr->suffix() == u"jpg") {
if (presentImages.find(itr->getPath()) == presentImages.end()) {
uint64_t id = images->addResource(itr->getPath());
foundImages.insert(std::make_pair(itr->parentDirectory(), id));
}
}
}
emit serviceMessage(QString("Found ") + std::to_string(foundAudios.size()).c_str() + " audio files");
emit serviceMessage(QString("Found ") + std::to_string(foundImages.size()).c_str() + " images");
state = updatingMusicDataBase;
updateMusicDataBase();
} else {
emit serviceMessage("Error parsing the directory");
}
delete list;
}
void Parser::updateMusicDataBase()
{
while (foundAudios.size() > 0) {
std::map<AudioId, uint64_t>::const_iterator itr = foundAudios.begin();
std::set<uint64_t> aids = artists.find(W::String(u"name"), itr->first.artist);
if (aids.size() == 0) {
W::Vocabulary art;
art.insert(u"name", itr->first.artist);
artists.addRemoteElement(art);
artistsReady = false;
emit serviceMessage(QString("Creating artist: ") + itr->first.artist.toString().c_str());
return;
}
uint64_t artistId = *(aids.begin());
uint64_t thumbId = 0; //TODO make some default picture for the case of not found images
std::set<uint64_t> alids = albums.find(W::String(u"name"), itr->first.album);
std::map<W::String, uint64_t>::const_iterator albImageItr = foundImages.find(itr->first.album);
if (albImageItr != foundImages.end()) {
thumbId = albImageItr->second;
}
uint64_t albumId = 0;
bool albumFound = false;
const C::Vocabulary* albCtrl = 0;
while (alids.size() > 0 && !albumFound) {
std::set<uint64_t>::const_iterator litr = alids.begin();
albumId = *litr;
alids.erase(litr);
albCtrl = &albums.get(albumId);
if (static_cast<const W::Uint64&>(albCtrl->at(u"artist")) == artistId) {
albumFound = true;
}
}
if (!albumFound) {
W::Vocabulary alb;
alb.insert(u"name", itr->first.album);
alb.insert(u"artist", W::Uint64(artistId));
if (thumbId != 0) {
alb.insert(u"image", W::Uint64(thumbId));
emit serviceMessage(QString("Found a cover for album: ") + itr->first.album.toString().c_str());
}
albums.addRemoteElement(alb);
albumsReady = false;
emit serviceMessage(QString("Creating album: ") + itr->first.album.toString().c_str());
return;
}
if (thumbId != 0 && (!albCtrl->has(u"image") || static_cast<const W::Uint64&>(albCtrl->at(u"image")) != thumbId)) {
W::Vocabulary alb;
alb.insert(u"image", W::Uint64(thumbId));
albums.updateRemoteElement(W::Uint64(albumId), alb);
emit serviceMessage(QString("Found a cover for album: ") + itr->first.album.toString().c_str());
foundImages.erase(albImageItr);
}
std::set<uint64_t> sids = songs.find(W::String(u"name"), itr->first.name);
uint64_t songId = 0;
bool songFound = false;
const C::Vocabulary* songCtrl = 0;
while (sids.size() > 0 && !songFound) {
std::set<uint64_t>::const_iterator sitr = sids.begin();
songId = *sitr;
sids.erase(sitr);
songCtrl = &songs.get(songId);
if (static_cast<const W::Uint64&>(songCtrl->at(u"album")) == albumId && static_cast<const W::Uint64&>(songCtrl->at(u"artist")) == artistId) {
songFound = true;
}
}
W::Vocabulary sng;
sng.insert(u"audio", W::Uint64(itr->second));
if (!songFound) {
sng.insert(u"name", itr->first.name);
sng.insert(u"album", W::Uint64(albumId));
sng.insert(u"artist", W::Uint64(artistId));
songs.addRemoteElement(sng);
songsReady = false;
emit serviceMessage(QString("Creating a song: ") + itr->first.name.toString().c_str());
} else if (!songCtrl->has(u"audio") || static_cast<const W::Uint64&>(songCtrl->at(u"audio")) != itr->second) {
emit serviceMessage(QString("Found missing media for a song: ") + itr->first.name.toString().c_str());
songs.updateRemoteElement(W::Uint64(songId), sng);
}
foundAudios.erase(itr);
if (!songFound) {
return;
}
}
emit serviceMessage("Audio parsing is complete");
state = updatingImageDataBase;
emit serviceMessage("Parsing images");
updateImageDataBase();
}
void Parser::updateImageDataBase()
{
while (foundImages.size() > 0) {
std::map<W::String, uint64_t>::const_iterator itr = foundImages.begin();
std::set<uint64_t> alids = albums.find(W::String(u"name"), itr->first);
if (alids.size() == 0) {
emit serviceMessage(QString("Image in the folder ") + itr->first.toString().c_str() + " doesn't belong to any albumm, skipping");
} else if (alids.size() > 1) {
emit serviceMessage(QString("Image in the folder ") + itr->first.toString().c_str() + " belongs to " + std::to_string(alids.size()).c_str() + " albums, skipping");
} else {
uint64_t albumId = *alids.begin();
const C::Vocabulary& ctrl = albums.get(albumId);
if (!ctrl.has(u"image") || static_cast<const W::Uint64&>(ctrl.at(u"image")) != itr->second) {
W::Vocabulary vc;
vc.insert(u"image", W::Uint64(itr->second));
emit serviceMessage(QString("Found missing cover for album: ") + itr->first.toString().c_str());
albums.updateRemoteElement(W::Uint64(albumId), vc);
}
}
foundImages.erase(itr);
}
emit serviceMessage("Parsing is complete");
state = idle;
emit done(path);
}

71
corax/tools/parser.h Normal file
View file

@ -0,0 +1,71 @@
#ifndef PARSER_H
#define PARSER_H
/**
* @todo write docs
*/
#include <map>
#include <QtCore/QObject>
#include <wType/string.h>
#include <wType/address.h>
#include <wSocket/socket.h>
#include <wDispatcher/dispatcher.h>
#include <wController/collection.h>
#include <wDatabase/resourcecache.h>
#include <tools/file.h>
#include "audiotag.h"
#include "audioid.h"
class Parser: public QObject
{
Q_OBJECT
public:
Parser(const W::Socket* p_socket, W::Dispatcher* p_dp, ResourceCache* p_audio, ResourceCache* p_images);
~Parser();
void parse(const W::String& p_path);
signals:
void serviceMessage(const QString& msg);
void done(const W::String& path);
private:
enum State {
idle,
waitingForCollections,
parsingDirectory,
updatingMusicDataBase,
updatingImageDataBase
};
const W::Socket* socket;
W::Dispatcher* dp;
C::Collection songs;
C::Collection albums;
C::Collection artists;
ResourceCache* audio;
ResourceCache* images;
W::String path;
bool songsReady;
bool albumsReady;
bool artistsReady;
State state;
std::map<W::String, uint64_t> foundImages;
std::map<AudioId, uint64_t> foundAudios;
void checkState();
void parseDirectory();
void updateMusicDataBase();
void updateImageDataBase();
private slots:
void onSongsReady();
void onAlbumsReady();
void onArtistsReady();
};
#endif // PARSER_H