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

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build/
cmake-build-debug/
cmake/

2
.idea/Eliya.iml Normal file
View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

7
.idea/misc.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Eliya.iml" filepath="$PROJECT_DIR$/.idea/Eliya.iml" />
</modules>
</component>
</project>

151
.idea/workspace.xml Normal file
View file

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeRunConfigurationManager" shouldGenerate="true" shouldDeleteObsolete="true">
<generated>
<config projectName="Eliya" targetName="Eliya" />
</generated>
</component>
<component name="CMakeSettings" AUTO_RELOAD="true">
<configurations>
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="ChangeListManager">
<list default="true" id="87f1c914-4d46-4c1a-9e29-30eb4356db3e" name="Default Changelist" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="ExecutionTargetManager" SELECTED_TARGET="CMakeBuildProfile:Debug" />
<component name="ProjectId" id="1YAFiGju36yXjkmzf3LxUY6eROH" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showExcludedFiles" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="settings.editor.selected.configurable" value="preferences.lookFeel" />
</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/cmake" />
<recent name="$PROJECT_DIR$/src" />
</key>
</component>
<component name="RunManager">
<configuration name="Eliya" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="--files=&quot;/mnt/nas/Bootleg/&quot;" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Eliya" TARGET_NAME="Eliya" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Eliya" RUN_TARGET_NAME="Eliya">
<method v="2">
<option name="com.jetbrains.cidr.execution.CidrBuildBeforeRunTaskProvider$BuildBeforeRunTask" enabled="true" />
</method>
</configuration>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="87f1c914-4d46-4c1a-9e29-30eb4356db3e" name="Default Changelist" comment="" />
<created>1582377616132</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1582377616132</updated>
<workItem from="1582377618031" duration="22316000" />
<workItem from="1582452152884" duration="8183000" />
<workItem from="1582551176378" duration="21273000" />
<workItem from="1582908123245" duration="33000" />
<workItem from="1583009998226" duration="6755000" />
<workItem from="1583055740209" duration="1581000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="WindowStateProjectService">
<state x="1251" y="400" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1583011913205">
<screen x="0" y="0" width="2560" height="1408" />
</state>
<state x="738" y="354" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582396273706" />
<state x="1251" y="400" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583011913205" />
<state x="3648" y="423" width="374" height="568" key="#com.intellij.ide.util.MemberChooser" timestamp="1582459657179">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state x="3648" y="423" width="374" height="568" key="#com.intellij.ide.util.MemberChooser/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1582459657179" />
<state width="2510" height="408" key="GridCell.Tab.0.bottom" timestamp="1583016802112">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state width="2510" height="482" key="GridCell.Tab.0.bottom/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582398707113" />
<state width="2510" height="692" key="GridCell.Tab.0.bottom/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583013554049" />
<state width="2510" height="408" key="GridCell.Tab.0.bottom/0.0.2560.1440/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583016802112" />
<state width="2510" height="408" key="GridCell.Tab.0.center" timestamp="1583016802112">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state width="2510" height="482" key="GridCell.Tab.0.center/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582398707113" />
<state width="2510" height="692" key="GridCell.Tab.0.center/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583013554049" />
<state width="2510" height="408" key="GridCell.Tab.0.center/0.0.2560.1440/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583016802112" />
<state width="2510" height="408" key="GridCell.Tab.0.left" timestamp="1583016802112">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state width="2510" height="482" key="GridCell.Tab.0.left/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582398707113" />
<state width="2510" height="692" key="GridCell.Tab.0.left/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583013554049" />
<state width="2510" height="408" key="GridCell.Tab.0.left/0.0.2560.1440/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583016802112" />
<state width="2510" height="408" key="GridCell.Tab.0.right" timestamp="1583016802112">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state width="2510" height="482" key="GridCell.Tab.0.right/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582398707113" />
<state width="2510" height="692" key="GridCell.Tab.0.right/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583013554049" />
<state width="2510" height="408" key="GridCell.Tab.0.right/0.0.2560.1440/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583016802112" />
<state width="2510" height="692" key="GridCell.Tab.1.bottom" timestamp="1583016802110">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state width="2510" height="453" key="GridCell.Tab.1.bottom/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582398641044" />
<state width="2510" height="692" key="GridCell.Tab.1.bottom/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583013554049" />
<state width="2510" height="692" key="GridCell.Tab.1.bottom/0.0.2560.1440/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583016802110" />
<state width="2510" height="692" key="GridCell.Tab.1.center" timestamp="1583016802110">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state width="2510" height="453" key="GridCell.Tab.1.center/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582398641043" />
<state width="2510" height="692" key="GridCell.Tab.1.center/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583013554049" />
<state width="2510" height="692" key="GridCell.Tab.1.center/0.0.2560.1440/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583016802110" />
<state width="2510" height="692" key="GridCell.Tab.1.left" timestamp="1583016802110">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state width="2510" height="453" key="GridCell.Tab.1.left/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582398641043" />
<state width="2510" height="692" key="GridCell.Tab.1.left/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583013554049" />
<state width="2510" height="692" key="GridCell.Tab.1.left/0.0.2560.1440/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583016802110" />
<state width="2510" height="692" key="GridCell.Tab.1.right" timestamp="1583016802110">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state width="2510" height="453" key="GridCell.Tab.1.right/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582398641044" />
<state width="2510" height="692" key="GridCell.Tab.1.right/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583013554049" />
<state width="2510" height="692" key="GridCell.Tab.1.right/0.0.2560.1440/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583016802110" />
<state x="3321" y="339" width="1037" height="736" key="SettingsEditor" timestamp="1582384592156">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state x="3321" y="339" width="1037" height="736" key="SettingsEditor/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1582384592156" />
<state x="3493" y="308" width="683" height="812" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser" timestamp="1582562732167">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state x="933" y="301" width="683" height="812" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582391546963" />
<state x="3493" y="308" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1582562732167" />
<state x="3487" y="384" width="705" height="640" key="find.popup" timestamp="1583013464055">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state x="3487" y="384" width="705" height="640" key="find.popup/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583013464055" />
<state x="3556" y="339" width="558" height="735" key="refactoring.ChangeSignatureDialog" timestamp="1583011117198">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state x="3556" y="339" width="558" height="735" key="refactoring.ChangeSignatureDialog/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583011117198" />
<state x="3503" y="321" width="672" height="678" key="search.everywhere.popup" timestamp="1583014420663">
<screen x="2560" y="0" width="2560" height="1440" />
</state>
<state x="943" y="314" width="672" height="663" key="search.everywhere.popup/0.0.2560.1408/2560.0.2560.1440@0.0.2560.1408" timestamp="1582398628799" />
<state x="3503" y="321" width="672" height="678" key="search.everywhere.popup/0.0.2560.1408/2560.0.2560.1440@2560.0.2560.1440" timestamp="1583014420663" />
</component>
</project>

12
CMakeLists.txt Normal file
View file

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.15)
project(Eliya VERSION 1.0)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${ECM_MODULE_PATH})
FILE(GLOB_RECURSE SRC "src/**/*.cpp" "src/**/*.h")
add_executable(Eliya src/main.cpp lib/json.hpp ${SRC})
find_package(TagLib REQUIRED)
find_package (Threads)
target_include_directories(Eliya PRIVATE ${TAGLIB_INCLUDE_DIRS})
target_link_libraries(Eliya ${TAGLIB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})

37
backup.txt Normal file
View file

@ -0,0 +1,37 @@
for (const auto &entry : fs::recursive_directory_iterator(inputDir, fs::directory_options::skip_permission_denied)) {
auto file = entry.path().string();
// doesnt need audio properties here
auto tagFile = TagLib::FileRef(file.c_str(), false);
if (!tagFile.isNull()) {
TagLib::MPEG::File f(file.c_str());
if (f.isValid()) {
TagLib::ID3v2::Tag *id3v2tag = f.ID3v2Tag();
if (id3v2tag) {
auto listOfFrames = id3v2tag->frameListMap()["APIC"];
if (!listOfFrames.isEmpty()) {
//hold here for debugger ;)
auto it = listOfFrames.begin();
for (; it != listOfFrames.end(); it++) {
auto *pictureFrame = dynamic_cast<TagLib::ID3v2::AttachedPictureFrame *> (*it);
/* {
FILE *jpegFile;
jpegFile = fopen("/home/versustune/FromId3.jpg", "wb");
unsigned long Size = pictureFrame->picture().size();
void *SrcImage = malloc(Size);
if (SrcImage) {
memcpy(SrcImage, pictureFrame->picture().data(), Size);
fwrite(SrcImage, Size, 1, jpegFile);
fclose(jpegFile);
free(SrcImage);
}
}*/
}
}
} else {
std::cout << "no ID3v2 \n";
}
} else {
std::cout << "is not Mpeg!" << "\n";
}
}
}

22875
lib/json.hpp Normal file

File diff suppressed because it is too large Load diff

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;
}