initial commit
This commit is contained in:
commit
4b60ece582
327 changed files with 28286 additions and 0 deletions
40
corax/CMakeLists.txt
Normal file
40
corax/CMakeLists.txt
Normal 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
236
corax/corax.cpp
Normal 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
88
corax/corax.h
Normal 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
18
corax/main.cpp
Normal 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
61
corax/tools/audioid.cpp
Normal 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
28
corax/tools/audioid.h
Normal 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
35
corax/tools/audiotag.cpp
Normal 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
29
corax/tools/audiotag.h
Normal 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
309
corax/tools/parser.cpp
Normal 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
71
corax/tools/parser.h
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue