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

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