first commit
This commit is contained in:
commit
0b15394d5d
27 changed files with 23984 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
build/
|
||||
cmake-build-debug/
|
||||
cmake/
|
2
.idea/Eliya.iml
Normal file
2
.idea/Eliya.iml
Normal 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
7
.idea/misc.xml
Normal 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
8
.idea/modules.xml
Normal 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
151
.idea/workspace.xml
Normal 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="/mnt/nas/Bootleg/"" 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
12
CMakeLists.txt
Normal 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
37
backup.txt
Normal 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
22875
lib/json.hpp
Normal file
File diff suppressed because it is too large
Load diff
60
src/Core/Arguments.cpp
Normal file
60
src/Core/Arguments.cpp
Normal 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
29
src/Core/Arguments.h
Normal 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
29
src/Core/Logo.h
Normal 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
5
src/Models/AudioFile.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
//
|
||||
// Created by versustune on 22.02.20.
|
||||
//
|
||||
|
||||
#include "AudioFile.h"
|
34
src/Models/AudioFile.h
Normal file
34
src/Models/AudioFile.h
Normal 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
142
src/Reader/FileReader.cpp
Normal 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
49
src/Reader/FileReader.h
Normal 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
|
18
src/Reader/FileValidator.cpp
Normal file
18
src/Reader/FileValidator.cpp
Normal 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();
|
||||
}
|
33
src/Reader/FileValidator.h
Normal file
33
src/Reader/FileValidator.h
Normal 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
59
src/Reader/ThreadPool.cpp
Normal 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
40
src/Reader/ThreadPool.h
Normal 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
|
101
src/Reader/ThreadSafeDeque.h
Normal file
101
src/Reader/ThreadSafeDeque.h
Normal 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
13
src/Utils/Logging.cpp
Normal 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
21
src/Utils/Logging.h
Normal 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
155
src/Utils/ProgressBar.cpp
Normal 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
38
src/Utils/ProgressBar.h
Normal 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
17
src/Utils/Utils.cpp
Normal 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
17
src/Utils/Utils.h
Normal 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
29
src/main.cpp
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue