first commit

This commit is contained in:
VersusTune 2020-04-03 13:26:16 +02:00
commit 0b15394d5d
27 changed files with 23984 additions and 0 deletions

60
src/Core/Arguments.cpp Normal file
View file

@ -0,0 +1,60 @@
//
// Created by versustune on 22.02.20.
//
#include "Arguments.h"
#include <cstring>
#include <utility>
/**
* PUBLIC ONES
*/
Arguments::Arguments(int argc, std::vector<std::string> args) {
count = argc;
Arguments::args = std::move(args);
}
Arguments::~Arguments() {
}
void Arguments::parseArgs() {
for (int i = 0; i < count; i++) {
//always skip the first :)
if (i == 0)
continue;
std::string string = std::string(args[i]);
//if argument doesnt start with -- it's invalid!
if (string.find("--", 0) != 0)
continue;
string = string.replace(0, 2, "");
auto pos = string.find('=', 0);
if (pos != 0 && pos < string.size()) {
std::string token = string.substr(0, pos);
parsedArgs[token] = string.substr(pos + 1, string.back());
} else {
parsedArgs[string] = "true";
}
}
}
std::string Arguments::getArg(const std::string &key) {
if (parsedArgs.find(key) == parsedArgs.end()) {
return "";
}
return parsedArgs[key];
}
std::string Arguments::getArgOrDefault(const std::string &key, std::string fallback) {
if (parsedArgs.find(key) == parsedArgs.end()) {
return fallback;
}
return parsedArgs[key];
}
const std::unordered_map<std::string, std::string> &Arguments::getParsedArgs() {
return parsedArgs;
}
/**
* PROTECTED ONES
*/

29
src/Core/Arguments.h Normal file
View file

@ -0,0 +1,29 @@
//
// Created by versustune on 22.02.20.
//
#ifndef ELIYA_ARGUMENTS_H
#define ELIYA_ARGUMENTS_H
#include <unordered_map>
#include <vector>
class Arguments {
private:
std::unordered_map<std::string, std::string> parsedArgs;
int count = 0;
std::vector<std::string> args;
public:
Arguments(int argc, std::vector<std::string> args);
~Arguments();
//pureRead args with this format: --key=args or --debug if "--" not defined it's says failed and skip it.. so make sure it's provided
void parseArgs();
std::string getArg(const std::string &key);
std::string getArgOrDefault(const std::string &key, std::string fallback);
const std::unordered_map<std::string, std::string> &getParsedArgs();
protected:
};
#endif //ELIYA_ARGUMENTS_H

29
src/Core/Logo.h Normal file
View file

@ -0,0 +1,29 @@
//
// Created by versustune on 22.02.20.
//
#ifndef ELIYA_LOGO_H
#define ELIYA_LOGO_H
#include <string>
#include <iostream>
class EliyaLogo {
private:
static std::string logo;
public:
EliyaLogo() {}
static void draw() {
std::cout << "\033[1;36m" << logo << "\033[0m\n\n";
}
};
std::string EliyaLogo::logo = "U _____ u _ __ __ _ \n"
"\\| ___\"|/ |\"| ___ \\ \\ / /U /\"\\ u \n"
" | _|\" U | | u |_\"_| \\ V / \\/ _ \\/ \n"
" | |___ \\| |/__ | | U_|\"|_u / ___ \\ \n"
" |_____| |_____| U/| |\\u |_| /_/ \\_\\ \n"
" << >> // \\\\.-,_|___|_,-.-,//|(_ \\\\ >> \n"
"(__) (__)(_\")(\"_)\\_)-' '-(_/ \\_) (__)(__) (__) ";
#endif //ELIYA_LOGO_H

5
src/Models/AudioFile.cpp Normal file
View file

@ -0,0 +1,5 @@
//
// Created by versustune on 22.02.20.
//
#include "AudioFile.h"

34
src/Models/AudioFile.h Normal file
View file

@ -0,0 +1,34 @@
//
// Created by versustune on 22.02.20.
//
#ifndef ELIYA_AUDIOFILE_H
#define ELIYA_AUDIOFILE_H
#include <string>
class AudioFile {
public:
AudioFile() = default;
~AudioFile() = default;
//properties
std::string path;
std::string title;
std::string artist;
std::string album;
std::string genre;
int year;
int track;
int length;
int bitrate;
int sampleRate;
int channels;
int bitsPerSample = -1; //not always available so -1 is empty
int samplesPerFrame = -1; // also not always available so set it to -1 that we know it's invalid ;)
bool isValid = false; //set to true if the audio file is valid
};
#endif //ELIYA_AUDIOFILE_H

142
src/Reader/FileReader.cpp Normal file
View file

@ -0,0 +1,142 @@
//
// Created by versustune on 22.02.20.
//
#include <iostream>
#include <algorithm>
#include <fstream>
#include <utility>
#include <zconf.h>
#include <queue>
#include "FileReader.h"
#include "../Utils/Logging.h"
#include "../Utils/Utils.h"
#include "../Utils/ProgressBar.h"
#include "ThreadPool.h"
#define FS_MODULE_NAME "FileReader"
FileReader::FileReader(std::string path, std::string outputPath) {
FileReader::path = std::move(path);
FileReader::outputPath = std::move(outputPath);
validateDirectories();
}
FileReader::~FileReader() {
}
const std::vector<AudioFile *> &FileReader::getValidFiles() const {
return validFiles;
}
// here i want to iterate over all files recursive and save all valid files ;)
void FileReader::pureRead() {
auto *progressBar = getProgressBar("Read Files", -1);
progressBar->startThread();
for (const auto &entry : fs::recursive_directory_iterator(path, fs::directory_options::skip_permission_denied)) {
if (fs::is_directory(entry.path()))
continue;
try {
//check if file is less then 2GB
if (entry.file_size() < 2147483648) {
files.push_back(entry.path());
}
} catch (fs::filesystem_error &error) {
//skip
}
progressBar->tick();
}
progressBar->setFinished(true);
while (!progressBar->isDone()) {
usleep(1);
}
}
void FileReader::validateDirectories() {
if (!fs::is_directory(path)) {
Logging::error("Input Path not Found", FS_MODULE_NAME);
exit(1);
}
if (!fs::is_directory(outputPath)) {
Logging::error("Output Path not Found. Creating? [Y/n]", FS_MODULE_NAME);
auto value = std::cin.get();
if (value == 110) {
exit(1);
}
fs::create_directory(outputPath);
if (!fs::is_directory(outputPath)) {
Logging::error("Cannot create Output Path", FS_MODULE_NAME);
exit(1);
}
}
auto cacheDir = outputPath + "/cache";
if (!fs::is_directory(cacheDir)) {
fs::create_directory(cacheDir);
if (!fs::is_directory(cacheDir)) {
Logging::error("Cannot create Cache Directory", FS_MODULE_NAME);
exit(1);
}
}
auto finalDir = outputPath + "/final";
if (!fs::is_directory(finalDir)) {
fs::create_directory(outputPath + "/final");
if (!fs::is_directory(finalDir)) {
Logging::error("Cannot create final Directory", FS_MODULE_NAME);
exit(1);
}
}
}
/*
* this function write a json from all valid music files into the cache directory.
* here we go. we get all AudioFile and call the toJson function.
*/
void FileReader::writeToCache() {
auto cacheFile = outputPath + "/cache/" + cacheName;
std::ofstream outputFile;
outputFile.open(cacheFile);
if (!outputFile.is_open()) {
Logging::error("Cannot open cache File: \"" + cacheFile + "\"", FS_MODULE_NAME);
Logging::error("Writing Cache FAILED", FS_MODULE_NAME);
return;
}
}
/*
* filename: stringToHex(inputPath) + ".json"
*/
std::string FileReader::getCacheName() {
if (cacheName.empty()) {
std::string _cacheName = path;
_cacheName.erase(std::remove_if(_cacheName.begin(), _cacheName.end(), ::isspace), cacheName.end());
cacheName = Utils::stringToHex(_cacheName) + ".json";
}
return cacheName;
}
void FileReader::readAudioFiles() {
auto *progressBar = getProgressBar("Validate Files", files.size());
auto threadPool = ThreadPool();
auto queue = new Tsqueue<std::shared_ptr<ThreadQueueItem>>();
for (int i = 0; i < files.size(); ++i) {
auto item = std::make_shared<ThreadQueueItem>();
item->fileIndex = i;
item->reader = this;
item->progressBar = progressBar;
queue->push(item);
}
progressBar->setMaxTicks(queue->getSize());
threadPool.setQueue(queue);
threadPool.start(progressBar);
progressBar->setFinished(true);
}
ProgressBar *FileReader::getProgressBar(std::string name, int maxTicks) {
auto progressBar = new ProgressBar(std::move(name), maxTicks, 1);
std::thread progressBarThread(&ProgressBar::runInThread, progressBar);
progressBarThread.detach();
return progressBar;
}

49
src/Reader/FileReader.h Normal file
View file

@ -0,0 +1,49 @@
//
// Created by versustune on 22.02.20.
//
#ifndef ELIYA_FILEREADER_H
#define ELIYA_FILEREADER_H
#include <filesystem>
#include <string>
#include <memory>
#include <vector>
#include <thread>
#include "../Models/AudioFile.h"
#include "FileValidator.h"
#include "../Utils/ProgressBar.h"
namespace fs = std::filesystem;
/*
* This class pureRead recursive from the disk and build and vector with all files.
*/
class FileReader {
private:
std::string path;
std::string outputPath;
std::string cacheName = "";
std::vector<AudioFile*> validFiles;
FileValidator validator;
public:
FileReader(std::string path, std::string outputPath);
~FileReader();
void pureRead();
void readAudioFiles();
const std::vector<AudioFile*> &getValidFiles() const;
std::vector<std::string> files; //clean after audioFile reading because can need alot of memory!
std::vector<std::string> processedFiles;
protected:
void validateDirectories();
void writeToCache();
std::string getCacheName();
static ProgressBar *getProgressBar(std::string name, int maxTicks);
};
#endif //ELIYA_FILEREADER_H

View file

@ -0,0 +1,18 @@
//
// Created by versustune on 24.02.20.
//
#include "FileValidator.h"
ValidateObject *FileValidator::validate(const std::string &path) {
auto validatedObject = new ValidateObject();
validatedObject->ref = TagLib::FileRef(path.c_str(), false);
validatedObject->isValid = !validatedObject->ref.isNull();
return validatedObject;
}
// in this file we build a AudioFile from TagLib...
// we know that object is always a ptr never a nullptr so we can use that shit again ;)
AudioFile FileValidator::getAudioFile(ValidateObject *object) {
return AudioFile();
}

View file

@ -0,0 +1,33 @@
//
// Created by versustune on 24.02.20.
//
#ifndef ELIYA_FILEVALIDATOR_H
#define ELIYA_FILEVALIDATOR_H
#include "../Models/AudioFile.h"
#include <taglib/fileref.h>
/*
* this class validate if the file is a audio file
*/
struct ValidateObject {
TagLib::FileRef ref;
bool isValid = true;
};
class FileValidator {
private:
AudioFile defaultFile;
public:
FileValidator() = default;
~FileValidator() = default;
ValidateObject * validate(const std::string& path);
AudioFile getAudioFile(ValidateObject *object);
protected:
};
#endif //ELIYA_FILEVALIDATOR_H

59
src/Reader/ThreadPool.cpp Normal file
View file

@ -0,0 +1,59 @@
//
// Created by versustune on 24.02.20.
//
#include "ThreadPool.h"
#include "../Utils/Logging.h"
#include "ThreadSafeDeque.h"
#include <thread>
#include <sstream>
#include <zconf.h>
void ThreadPool::setQueue(Tsqueue<std::shared_ptr<ThreadQueueItem>> *queue) {
ThreadPool::threadQueue = queue;
}
void ThreadPool::initThreads() {
threadCount = std::thread::hardware_concurrency();
std::stringstream stream;
stream << "Create Threadpool with " << threadCount << " threads";
Logging::log(stream.str().c_str(), "ThreadPool");
threads.resize(threadCount);
threads.clear();
finishedThreads.clear();
for (int i = 0; i < threadCount; ++i) {
threads[i] = new std::thread(&ThreadPool::execute, this, i);
threads[i]->detach();
}
}
void ThreadPool::start(ProgressBar *progressBar) {
initThreads();
progressBar->startThread();
while (finishedThreads.size() < threadCount) {
//wait for threads to finish
std::this_thread::sleep_for(std::chrono::microseconds(1000));
}
progressBar->update();
}
void ThreadPool::execute(int threadNumber) {
//if queue empty terminate Thread... make no sense ;)
if (threadQueue->getSize() < 1) {
return;
}
while (auto queueItem = threadQueue->pop()) {
auto item = queueItem.value();
if (item == nullptr) {
continue;
}
auto file = item->reader->files[item->fileIndex];
auto wait = 10000L+(long)((1e5-1e4)*random()/(RAND_MAX+1.0));
usleep(wait);
//do your job here!
item->progressBar->tick();
}
finishedThreads.push_back(threadNumber + 1);
}

40
src/Reader/ThreadPool.h Normal file
View file

@ -0,0 +1,40 @@
//
// Created by versustune on 24.02.20.
//
#ifndef ELIYA_THREADPOOL_H
#define ELIYA_THREADPOOL_H
#include <queue>
#include <thread>
#include "../Utils/ProgressBar.h"
#include "FileReader.h"
#include "ThreadSafeDeque.h"
struct ThreadQueueItem {
ProgressBar *progressBar = nullptr;
int fileIndex{};
int status{};
FileReader *reader = nullptr;
ThreadQueueItem() = default;
};
class ThreadPool {
private:
int threadCount = 12;
std::vector<std::thread*> threads;
Tsqueue<std::shared_ptr<ThreadQueueItem>> *threadQueue = nullptr;
std::vector<int> finishedThreads;
public:
ThreadPool() = default;
~ThreadPool() = default;
void setQueue(Tsqueue<std::shared_ptr<ThreadQueueItem>> *queue);
void initThreads();
void start(ProgressBar * progressBar);
protected:
void execute(int threadNumber);
};
#endif //ELIYA_THREADPOOL_H

View file

@ -0,0 +1,101 @@
//
// Created by versustune on 29.02.20.
//
#ifndef ELIYA_THREADSAFEDEQUE_H
#define ELIYA_THREADSAFEDEQUE_H
#include <utility>
#include <atomic>
#include <queue>
#include <condition_variable>
#include <optional>
#include <cassert>
template<typename T>
struct Tsqueue {
/* Create Tsqueue object. Set maximum size of the queue to max_size. */
inline explicit Tsqueue(size_t max_size = -1UL) : maxsize(max_size), end(false) {};
/* Push T to the queue. Many threads can push at the same time.
* If the queue is full, calling thread will be suspended until
* some other thread pop() data. */
void push(const T &);
void push(T &&);
/* Close the queue.
* Be sure all writing threads done their writes before call this.
* Push data to closed queue is forbidden. */
void close();
/* Pop and return T from the queue. Many threads can pop at the same time.
* If the queue is empty, calling thread will be suspended.
* If the queue is empty and closed, nullopt returned. */
std::optional<T> pop();
std::optional<T> waitAndPop();
int getSize() {
return que.size();
}
private:
std::queue<T> que;
std::mutex mtx;
std::condition_variable cv_empty, cv_full;
const size_t maxsize;
std::atomic<bool> end;
};
template<typename T>
void Tsqueue<T>::push(T &&t) {
std::unique_lock<std::mutex> lck(mtx);
while (que.size() == maxsize && !end)
cv_full.wait(lck);
assert(!end);
que.push(std::move(t));
cv_empty.notify_one();
}
template<typename T>
void Tsqueue<T>::push(const T &t) {
std::unique_lock<std::mutex> lck(mtx);
while (que.size() == maxsize && !end)
cv_full.wait(lck);
assert(!end);
que.push(std::move(t));
cv_empty.notify_one();
}
template<typename T>
std::optional<T> Tsqueue<T>::pop() {
std::unique_lock<std::mutex> lck(mtx);
if (que.empty()) return {};
T t = std::move(que.front());
que.pop();
cv_full.notify_one();
return t;
}
template<typename T>
std::optional<T> Tsqueue<T>::waitAndPop() {
std::unique_lock<std::mutex> lck(mtx);
while (que.empty() && !end)
cv_empty.wait(lck);
if (que.empty()) return {};
T t = std::move(que.front());
que.pop();
cv_full.notify_one();
return t;
}
template<typename T>
void Tsqueue<T>::close() {
end = true;
std::lock_guard<std::mutex> lck(mtx);
cv_empty.notify_one();
cv_full.notify_one();
}
#endif //ELIYA_THREADSAFEDEQUE_H

13
src/Utils/Logging.cpp Normal file
View file

@ -0,0 +1,13 @@
//
// Created by versustune on 22.02.20.
//
#include "Logging.h"
void Logging::error(std::basic_string<char> error, std::basic_string<char> module) noexcept {
printf("\033[1;31m[Error][%s]\t %s\033[0m\n", module.c_str(), error.c_str());
}
void Logging::log(const char* log, const char* module) noexcept {
printf("\033[1;34m[Info]\033[0m[%s]\t %s\n", module, log);
}

21
src/Utils/Logging.h Normal file
View file

@ -0,0 +1,21 @@
//
// Created by versustune on 22.02.20.
//
#pragma once
#ifndef ELIYA_LOGGING_H
#define ELIYA_LOGGING_H
#include <string>
class Logging {
public:
static void error(std::basic_string<char> error, std::basic_string<char> module) noexcept;
static void log(const char* log, const char* module) noexcept;
};
#endif //ELIYA_LOGGING_H

155
src/Utils/ProgressBar.cpp Normal file
View file

@ -0,0 +1,155 @@
//
// Created by versustune on 23.02.20.
//
#include <iomanip>
#include <iostream>
#include <cmath>
#include <zconf.h>
#include <thread>
#include "ProgressBar.h"
ProgressBar::ProgressBar(std::string name, int maxTicks, int showAfterTicks) {
ProgressBar::maxTicks = maxTicks + 0;
ProgressBar::ticks = 0 + 0;
ProgressBar::name = "\033[1;32m" + std::move(name) + "\033[0m";
ProgressBar::showAfterTicks = showAfterTicks;
if (maxTicks == -1) {
isUnlimited = true;
}
}
ProgressBar::~ProgressBar() = default;
void ProgressBar::update() {
//only update if ticks reached
if (ticks % showAfterTicks == 0 || (isFinished && !finishedDraw)) {
if (isUnlimited) {
renderUnknown();
} else {
renderNormal();
}
if (isFinished) {
finishedDraw = true;
}
}
}
void ProgressBar::tick() {
++ticks;
if (ticks > maxTicks && maxTicks != -1) {
ticks = maxTicks;
isFinished = true;
}
}
void ProgressBar::setFinished(bool isThread) {
isFinished = true;
if (!isThread) {
this->update();
}
}
void ProgressBar::start() {
start_time = std::chrono::steady_clock::now();
this->update();
}
void ProgressBar::renderUnknown() {
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time).count();
double seconds = static_cast<double>(time_elapsed) / 1000.0;
double ticks_per_second = static_cast<double>(ticks) / seconds;
std::stringstream stream;
stream << "\033[2K" << "\r";
stream << "[" << name << "] \033[1;34m[" << unlimitedChars.at(++lastIcon % unlimitedChars.length());
stream << "]\033[1;36m [" << ticks << "]" << " (" << static_cast<std::size_t>(ticks ? ticks_per_second : 0.0)
<< " t/s)\033[0m";
std::cout << stream.str() << std::flush;
}
void ProgressBar::renderNormal() {
int pos = 0;
double progress = 0;
if (ticks > 0) {
progress = double(ticks) / double(maxTicks);
pos = (50 * progress);
}
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time).count();
double seconds = static_cast<double>(time_elapsed) / 1000.0;
double ticks_per_second = static_cast<double>(ticks) / seconds;
double remaining_ticks = maxTicks - ticks;
double total_seconds_rem = remaining_ticks / ticks_per_second;
std::stringstream stream;
stream << "\033[2K" << "\r";
stream << "[" << name << "]\033[1;34m[";
for (int i = 0; i < 50; ++i) {
if (i < pos) stream << '=';
else if (i == pos) stream << ">";
else stream << ' ';
}
stream << "]\033[1;35m " << std::roundf(progress * 100.0f) << "% ";
stream << ticks << " / " << maxTicks << " [";
convertTime(stream, seconds);
stream << " / ";
if (ticks) {
convertTime(stream, total_seconds_rem);
} else {
stream << "??:??:??";
}
stream << "]";
stream << " (" << static_cast<std::size_t>(ticks ? ticks_per_second : 0.0) << " t/s)\033[0m";
std::cout << stream.str() << std::flush;
}
void ProgressBar::convertTime(std::stringstream &stream, double seconds) {
std::size_t s_in_h(60 * 60), s_in_m(60)/*, m_in_h(60)*/;
int hours_rem = seconds / s_in_h;
int minutes_rem = (seconds - (hours_rem * s_in_h)) / s_in_m;
int seconds_rem = seconds - ((hours_rem * s_in_h) + (minutes_rem * s_in_m));
std::ios init(nullptr), time_fmt(nullptr);
init.copyfmt(stream);
stream << std::setfill('0') << std::setw(2);
time_fmt.copyfmt(stream);
//hours
stream.copyfmt(time_fmt);
stream << hours_rem;
stream.copyfmt(init);
//minutes
stream << ":";
stream.copyfmt(time_fmt);
stream << minutes_rem;
stream.copyfmt(init);
//seconds
stream << ":";
stream.copyfmt(time_fmt);
stream << seconds_rem;
//restore format
stream.copyfmt(init);
}
bool ProgressBar::isDone() const {
return isFinished && finishedDraw;
}
void ProgressBar::runInThread() {
while(!runThread) {
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
this->start();
while (!isDone()) {
this->update();
}
std::cout << "\n";
delete this;
}
void ProgressBar::setMaxTicks(int newMaxTicks) {
maxTicks = newMaxTicks;
}
void ProgressBar::startThread() {
runThread = true;
}

38
src/Utils/ProgressBar.h Normal file
View file

@ -0,0 +1,38 @@
//
// Created by versustune on 23.02.20.
//
#ifndef ELIYA_PROGRESSBAR_H
#define ELIYA_PROGRESSBAR_H
#include <string>
#include <chrono>
class ProgressBar {
private:
long ticks, maxTicks, showAfterTicks, lastIcon = 0;
bool isUnlimited = false, isFinished = false, finishedDraw = false;
std::string name;
std::chrono::steady_clock::time_point start_time;
std::string unlimitedChars = "|/-\\";
bool runThread = false;
public:
ProgressBar(std::string name, int maxTicks, int showAfterTicks);
~ProgressBar();
void start();
void update();
void setFinished(bool isThread);
void tick();
bool isDone() const;
void setMaxTicks(int newMaxTicks);
void runInThread();
void startThread();
protected:
void renderNormal();
void renderUnknown();
void convertTime(std::stringstream & stream, double seconds);
};
#endif //ELIYA_PROGRESSBAR_H

17
src/Utils/Utils.cpp Normal file
View file

@ -0,0 +1,17 @@
//
// Created by versustune on 23.02.20.
//
#include "Utils.h"
std::string Utils::stringToHex(const std::string &input) {
static const char hex_digits[] = "0123456789ABCDEF";
std::string output;
output.reserve(input.length() * 2);
for (unsigned char c : input) {
output.push_back(hex_digits[c >> 4]);
output.push_back(hex_digits[c & 15]);
}
return output;
}

17
src/Utils/Utils.h Normal file
View file

@ -0,0 +1,17 @@
//
// Created by versustune on 23.02.20.
//
#ifndef ELIYA_UTILS_H
#define ELIYA_UTILS_H
#include <string>
class Utils {
public:
static std::string stringToHex(const std::string &input);
};
#endif //ELIYA_UTILS_H

29
src/main.cpp Normal file
View file

@ -0,0 +1,29 @@
#include <iostream>
#include <vector>
#include "Core/Arguments.h"
#include "Core/Logo.h"
#include "Reader/FileReader.h"
#include "Utils/Logging.h"
int main(int argc, char *argv[]) {
std::cout << "\x1B[2J\x1B[H";
EliyaLogo::draw();
std::vector<std::string> args;
for (int i = 0; i < argc; i++)
args.emplace_back(argv[i]);
auto argParser = Arguments(argc, args);
argParser.parseArgs();
// test if the paths are provided
std::string inputDir = argParser.getArgOrDefault("files", "");
std::string opDir = argParser.getArgOrDefault("output", std::string(std::getenv("HOME")) + "/.eliya");
if (inputDir.empty() || opDir.empty()) {
Logging::error("No Input or Operation path specified", "ArgumentValidator");
exit(-1);
}
auto reader = FileReader(inputDir, opDir);
reader.pureRead();
reader.readAudioFiles();
return 0;
}