some more thoughts about config
This commit is contained in:
parent
870842f63d
commit
6c7356598a
@ -1,3 +1,10 @@
|
||||
function(make_includable input_file output_file)
|
||||
file(READ ${input_file} content)
|
||||
set(delim "for_c++_include")
|
||||
set(content "R\"${delim}(\n${content})${delim}\"")
|
||||
file(WRITE ${output_file} "${content}")
|
||||
endfunction(make_includable)
|
||||
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
help.cpp
|
||||
@ -20,3 +27,6 @@ set(HEADERS
|
||||
)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
|
||||
|
||||
make_includable(default.conf ${CMAKE_BINARY_DIR}/generated/default.conf)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR})
|
||||
|
@ -71,13 +71,12 @@ void Collection::convert(const std::string& outPath) {
|
||||
switch (entry.status().type()) {
|
||||
case fs::file_type::regular: {
|
||||
fs::path sourcePath = entry.path();
|
||||
fs::path dstPath = out / sourcePath.filename();
|
||||
if (isMusic(sourcePath)) {
|
||||
dstPath.replace_extension(".mp3");
|
||||
fs::path dstPath = out / sourcePath.stem();
|
||||
if (isMusic(sourcePath))
|
||||
taskManager->queueJob(sourcePath, dstPath);
|
||||
} else {
|
||||
else
|
||||
fs::copy_file(sourcePath, dstPath, fs::copy_options::overwrite_existing);
|
||||
}
|
||||
|
||||
//std::cout << sourcePath << " => " << dstPath << std::endl;
|
||||
} break;
|
||||
case fs::file_type::directory: {
|
||||
|
23
src/default.conf
Normal file
23
src/default.conf
Normal file
@ -0,0 +1,23 @@
|
||||
# This is a default autogenerated MLC config
|
||||
# The syntax goes like this:
|
||||
# key value
|
||||
#
|
||||
# Use # sign for comments
|
||||
#
|
||||
# All printed comented out values are default values
|
||||
#
|
||||
# You should have one of this files as ~/.config/mlc.conf,
|
||||
# if you have started mlc at least once.
|
||||
# This is going to be used every time you launch mlc,
|
||||
# so, edit it if you wish to change default behaviour.
|
||||
#
|
||||
# Alternatively, you can launch `mlc -c customConfig.conf`
|
||||
# if you wish to override ~/.config/mlc.conf file
|
||||
|
||||
# Log level, regulates minimal message severity to be printed
|
||||
# Allowed values are: debug, info, minor, major, warning, error, fatal
|
||||
#level info
|
||||
|
||||
# Output type
|
||||
# Allowed values are: mp3 (more comming soon)
|
||||
#type mp3
|
@ -1,5 +1,15 @@
|
||||
#include "loggable.h"
|
||||
|
||||
constexpr std::array<std::string_view, Loggable::_sevetirySize> levels({
|
||||
"debug",
|
||||
"info",
|
||||
"minor",
|
||||
"major",
|
||||
"warning",
|
||||
"error",
|
||||
"fatal"
|
||||
});
|
||||
|
||||
Loggable::Loggable(Loggable::Severity severity):
|
||||
currentSeverity(severity),
|
||||
history()
|
||||
@ -16,3 +26,11 @@ void Loggable::log(Loggable::Severity severity, const std::string& comment) cons
|
||||
std::list<std::pair<Loggable::Severity, std::string>> Loggable::getHistory() const {
|
||||
return history;
|
||||
}
|
||||
|
||||
Loggable::Severity Loggable::stringToSeverity(const std::string& line) {
|
||||
unsigned char dist = std::distance(levels.begin(), std::find(levels.begin(), levels.end(), line));
|
||||
if (dist < _sevetirySize)
|
||||
return static_cast<Severity>(dist);
|
||||
|
||||
return _sevetirySize;
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
|
||||
class Loggable {
|
||||
public:
|
||||
@ -12,7 +15,8 @@ public:
|
||||
major,
|
||||
warning,
|
||||
error,
|
||||
fatal
|
||||
fatal,
|
||||
_sevetirySize
|
||||
};
|
||||
typedef std::pair<Severity, std::string> Message;
|
||||
|
||||
@ -22,6 +26,8 @@ public:
|
||||
void log (Severity severity, const std::string& comment) const;
|
||||
std::list<Message> getHistory() const;
|
||||
|
||||
static Severity stringToSeverity(const std::string& line);
|
||||
|
||||
private:
|
||||
const Severity currentSeverity;
|
||||
mutable std::list<Message> history;
|
||||
|
27
src/main.cpp
27
src/main.cpp
@ -1,6 +1,8 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "FLAC/stream_decoder.h"
|
||||
@ -12,14 +14,14 @@
|
||||
#include "settings.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Settings settings(argc, argv);
|
||||
std::shared_ptr<Settings> settings = std::make_shared<Settings>(argc, argv);
|
||||
|
||||
switch (settings.getAction()) {
|
||||
switch (settings->getAction()) {
|
||||
case Settings::help:
|
||||
printHelp();
|
||||
return 0;
|
||||
case Settings::printConfig:
|
||||
//printHelp();
|
||||
case Settings::config:
|
||||
std::cout << settings->defaultConfig() << std::endl;
|
||||
return 0;
|
||||
case Settings::convert:
|
||||
std::cout << "Converting..." << std::endl;
|
||||
@ -29,12 +31,23 @@ int main(int argc, char **argv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
TaskManager taskManager;
|
||||
if (!settings->readConfigFile() && settings->isConfigDefault()) {
|
||||
std::string defaultConfigPath = settings->getConfigPath();
|
||||
std::ofstream file(defaultConfigPath, std::ios::out | std::ios::trunc);
|
||||
if (file.is_open()) {
|
||||
std::cout << "Writing default config to " << defaultConfigPath << std::endl;
|
||||
file << settings->defaultConfig();
|
||||
} else {
|
||||
std::cout << "Couldn't open " << defaultConfigPath << " to write default config" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
TaskManager taskManager(settings);
|
||||
taskManager.start();
|
||||
|
||||
std::chrono::time_point start = std::chrono::system_clock::now();
|
||||
Collection collection(settings.getInput(), &taskManager);
|
||||
collection.convert(settings.getOutput());
|
||||
Collection collection(settings->getInput(), &taskManager);
|
||||
collection.convert(settings->getOutput());
|
||||
|
||||
taskManager.printProgress();
|
||||
taskManager.wait();
|
||||
|
143
src/settings.cpp
143
src/settings.cpp
@ -1,14 +1,32 @@
|
||||
#include "settings.h"
|
||||
|
||||
static const char* defaultConfig =
|
||||
#include "generated/default.conf"
|
||||
;
|
||||
|
||||
constexpr std::array<std::string_view, Settings::_actionsSize> actions({
|
||||
"convert",
|
||||
"help",
|
||||
"printConfig"
|
||||
"config"
|
||||
});
|
||||
|
||||
constexpr std::array<std::string_view, Settings::_optionsSize> options({
|
||||
"level",
|
||||
"type"
|
||||
});
|
||||
|
||||
constexpr std::array<std::string_view, Settings::_typesSize> types({
|
||||
"mp3"
|
||||
});
|
||||
|
||||
bool is_space(char ch){
|
||||
return std::isspace(static_cast<unsigned char>(ch));
|
||||
}
|
||||
|
||||
Settings::Settings(int argc, char ** argv):
|
||||
arguments(),
|
||||
action(std::nullopt),
|
||||
outputType(std::nullopt),
|
||||
input(std::nullopt),
|
||||
output(std::nullopt),
|
||||
logLevel(std::nullopt)
|
||||
@ -23,10 +41,9 @@ void Settings::parseArguments() {
|
||||
for (int i = 0; i < arguments.size(); ++i) {
|
||||
const std::string_view& arg = arguments[i];
|
||||
if (i == 0) {
|
||||
std::string_view act = stripFlags(arg);
|
||||
int dist = std::distance(actions.begin(), std::find(actions.begin(), actions.end(), act));
|
||||
if (dist < _actionsSize) {
|
||||
action = static_cast<Settings::Action>(dist);
|
||||
Action act = stringToAction(stripFlags(arg));
|
||||
if (act < _actionsSize) {
|
||||
action = act;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -52,6 +69,13 @@ Settings::Action Settings::getAction() const {
|
||||
return convert;
|
||||
}
|
||||
|
||||
Settings::Type Settings::getType() const {
|
||||
if (outputType.has_value())
|
||||
return outputType.value();
|
||||
else
|
||||
return mp3;
|
||||
}
|
||||
|
||||
std::string Settings::getInput() const {
|
||||
if (input.has_value())
|
||||
return input.value();
|
||||
@ -66,6 +90,17 @@ std::string Settings::getOutput() const {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string Settings::getConfigPath() const {
|
||||
if (configPath.has_value())
|
||||
return configPath.value();
|
||||
else
|
||||
return std::string(getenv("HOME")) + "/.config/mlc.conf";
|
||||
}
|
||||
|
||||
bool Settings::isConfigDefault() const {
|
||||
return !configPath.has_value();
|
||||
}
|
||||
|
||||
Loggable::Severity Settings::getLogLevel() const {
|
||||
if (logLevel.has_value())
|
||||
return logLevel.value();
|
||||
@ -82,3 +117,101 @@ std::string_view Settings::stripFlags(const std::string_view& option) {
|
||||
}
|
||||
return option;
|
||||
}
|
||||
|
||||
void Settings::strip(std::string& line) {
|
||||
line.erase(line.begin(), std::find_if(line.begin(), line.end(), std::not_fn(is_space)));
|
||||
line.erase(std::find_if(line.rbegin(), line.rend(), std::not_fn(is_space)).base(), line.end());
|
||||
}
|
||||
|
||||
void Settings::stripComment(std::string& line) {
|
||||
std::string::size_type index = line.find('#');
|
||||
if (index != std::string::npos)
|
||||
line.erase(index);
|
||||
|
||||
strip(line);
|
||||
}
|
||||
|
||||
std::string Settings::defaultConfig() const {
|
||||
return ::defaultConfig + 1;
|
||||
}
|
||||
|
||||
bool Settings::readConfigFile() {
|
||||
std::ifstream file;
|
||||
file.open(getConfigPath(), std::ios::in);
|
||||
if (file.is_open()){
|
||||
std::string tp;
|
||||
while(getline(file, tp)) {
|
||||
stripComment(tp);
|
||||
if (!tp.empty())
|
||||
readConfigLine(tp);
|
||||
}
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Settings::readConfigLine(const std::string& line) {
|
||||
std::istringstream stream(line);
|
||||
std::string key;
|
||||
if (!(stream >> key))
|
||||
return;
|
||||
|
||||
Option option = stringToOption(key);
|
||||
if (option == _optionsSize)
|
||||
return;
|
||||
|
||||
switch (option) {
|
||||
case level: {
|
||||
std::string lv;
|
||||
if (stream >> lv) {
|
||||
Loggable::Severity level = Loggable::stringToSeverity(lv);
|
||||
if (level < Loggable::_sevetirySize)
|
||||
logLevel = level;
|
||||
}
|
||||
} break;
|
||||
case type: {
|
||||
std::string lv;
|
||||
if (stream >> lv) {
|
||||
Type type = stringToType(lv);
|
||||
if (type < _typesSize)
|
||||
outputType = type;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Settings::Action Settings::stringToAction(const std::string& source) {
|
||||
unsigned char dist = std::distance(actions.begin(), std::find(actions.begin(), actions.end(), source));
|
||||
if (dist < _actionsSize)
|
||||
return static_cast<Action>(dist);
|
||||
|
||||
return _actionsSize;
|
||||
}
|
||||
|
||||
Settings::Action Settings::stringToAction(const std::string_view& source) {
|
||||
unsigned char dist = std::distance(actions.begin(), std::find(actions.begin(), actions.end(), source));
|
||||
if (dist < _actionsSize)
|
||||
return static_cast<Action>(dist);
|
||||
|
||||
return _actionsSize;
|
||||
}
|
||||
|
||||
Settings::Option Settings::stringToOption(const std::string& source) {
|
||||
unsigned char dist = std::distance(options.begin(), std::find(options.begin(), options.end(), source));
|
||||
if (dist < _optionsSize)
|
||||
return static_cast<Option>(dist);
|
||||
|
||||
return _optionsSize;
|
||||
}
|
||||
|
||||
Settings::Type Settings::stringToType(const std::string& source) {
|
||||
unsigned char dist = std::distance(types.begin(), std::find(types.begin(), types.end(), source));
|
||||
if (dist < _typesSize)
|
||||
return static_cast<Type>(dist);
|
||||
|
||||
return _typesSize;
|
||||
}
|
||||
|
@ -6,6 +6,11 @@
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
#include "loggable.h"
|
||||
|
||||
@ -14,26 +19,53 @@ public:
|
||||
enum Action {
|
||||
convert,
|
||||
help,
|
||||
printConfig,
|
||||
config,
|
||||
_actionsSize
|
||||
};
|
||||
|
||||
enum Type {
|
||||
mp3,
|
||||
_typesSize
|
||||
};
|
||||
|
||||
enum Option {
|
||||
level,
|
||||
type,
|
||||
_optionsSize
|
||||
};
|
||||
|
||||
Settings(int argc, char **argv);
|
||||
|
||||
std::string getInput() const;
|
||||
std::string getOutput() const;
|
||||
std::string getConfigPath() const;
|
||||
bool isConfigDefault() const;
|
||||
Loggable::Severity getLogLevel() const;
|
||||
Type getType() const;
|
||||
Action getAction() const;
|
||||
|
||||
bool readConfigFile();
|
||||
void readConfigLine(const std::string& line);
|
||||
std::string defaultConfig() const;
|
||||
|
||||
static Action stringToAction(const std::string& source);
|
||||
static Action stringToAction(const std::string_view& source);
|
||||
static Type stringToType(const std::string& source);
|
||||
static Option stringToOption(const std::string& source);
|
||||
|
||||
private:
|
||||
void parseArguments();
|
||||
|
||||
static std::string_view stripFlags(const std::string_view& option);
|
||||
static void strip(std::string& line);
|
||||
static void stripComment(std::string& line);
|
||||
|
||||
private:
|
||||
std::vector<std::string_view> arguments;
|
||||
std::optional<Action> action;
|
||||
std::optional<Type> outputType;
|
||||
std::optional<std::string> input;
|
||||
std::optional<std::string> output;
|
||||
std::optional<Loggable::Severity> logLevel;
|
||||
std::optional<std::string> configPath;
|
||||
};
|
||||
|
@ -22,7 +22,8 @@ constexpr const std::array<std::string_view, Loggable::fatal + 1> logHeaders({
|
||||
/*fatal*/ "FATAL: "
|
||||
});
|
||||
|
||||
TaskManager::TaskManager():
|
||||
TaskManager::TaskManager(const std::shared_ptr<Settings>& settings):
|
||||
settings(settings),
|
||||
busyThreads(0),
|
||||
maxTasks(0),
|
||||
completeTasks(0),
|
||||
@ -42,7 +43,7 @@ TaskManager::TaskManager():
|
||||
TaskManager::~TaskManager() {
|
||||
}
|
||||
|
||||
void TaskManager::queueJob(const std::string& source, const std::string& destination) {
|
||||
void TaskManager::queueJob(const std::filesystem::path& source, const std::filesystem::path& destination) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
jobs.emplace(source, destination);
|
||||
@ -83,7 +84,13 @@ void TaskManager::loop() {
|
||||
jobs.pop();
|
||||
}
|
||||
|
||||
JobResult result = job(pair.first, pair.second);
|
||||
JobResult result;
|
||||
switch (settings->getType()) {
|
||||
case Settings::mp3:
|
||||
result = mp3Job(pair.first, pair.second + ".mp3");
|
||||
break;
|
||||
}
|
||||
|
||||
++completeTasks;
|
||||
printProgress(result, pair.first, pair.second);
|
||||
--busyThreads;
|
||||
@ -117,7 +124,7 @@ void TaskManager::wait() {
|
||||
waitConditional.wait(lock, boundWaitCondition);
|
||||
}
|
||||
|
||||
TaskManager::JobResult TaskManager::job(const std::string& source, const std::string& destination) {
|
||||
TaskManager::JobResult TaskManager::mp3Job(const std::filesystem::path& source, const std::filesystem::path& destination) {
|
||||
FLACtoMP3 convertor(Loggable::debug);
|
||||
convertor.setInputFile(source);
|
||||
convertor.setOutputFile(destination);
|
||||
@ -130,7 +137,11 @@ void TaskManager::printProgress() const {
|
||||
std::cout << "\r" << completeTasks << "/" << maxTasks << std::flush;
|
||||
}
|
||||
|
||||
void TaskManager::printProgress(const TaskManager::JobResult& result, const std::string& source, const std::string& destination) const {
|
||||
void TaskManager::printProgress(
|
||||
const TaskManager::JobResult& result,
|
||||
const std::filesystem::path& source,
|
||||
const std::filesystem::path& destination) const
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(printMutex);
|
||||
if (result.first) {
|
||||
if (result.second.size() > 0) {
|
||||
@ -144,7 +155,11 @@ void TaskManager::printProgress(const TaskManager::JobResult& result, const std:
|
||||
std::cout << "\r" << completeTasks << "/" << maxTasks << std::flush;
|
||||
}
|
||||
|
||||
void TaskManager::printLog(const TaskManager::JobResult& result, const std::string& source, const std::string& destination) {
|
||||
void TaskManager::printLog(
|
||||
const TaskManager::JobResult& result,
|
||||
const std::filesystem::path& source,
|
||||
const std::filesystem::path& destination)
|
||||
{
|
||||
std::cout << "Source: \t" << source << std::endl;
|
||||
std::cout << "Destination: \t" << destination << std::endl;
|
||||
for (const Loggable::Message& msg : result.second) {
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <condition_variable>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
@ -11,30 +12,33 @@
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "loggable.h"
|
||||
#include "settings.h"
|
||||
|
||||
class TaskManager {
|
||||
typedef std::pair<bool, std::list<Loggable::Message>> JobResult;
|
||||
public:
|
||||
TaskManager();
|
||||
TaskManager(const std::shared_ptr<Settings>& settings);
|
||||
~TaskManager();
|
||||
|
||||
void start();
|
||||
void queueJob(const std::string& source, const std::string& destination);
|
||||
void queueJob(const std::filesystem::path& source, const std::filesystem::path& destination);
|
||||
void stop();
|
||||
bool busy() const;
|
||||
void wait();
|
||||
void printProgress() const;
|
||||
void printProgress(const JobResult& result, const std::string& source, const std::string& destination) const;
|
||||
void printProgress(const JobResult& result, const std::filesystem::path& source, const std::filesystem::path& destination) const;
|
||||
|
||||
private:
|
||||
void loop();
|
||||
bool loopCondition() const;
|
||||
bool waitCondition() const;
|
||||
static JobResult job(const std::string& source, const std::string& destination);
|
||||
static void printLog(const JobResult& result, const std::string& source, const std::string& destination);
|
||||
static JobResult mp3Job(const std::filesystem::path& source, const std::filesystem::path& destination);
|
||||
static void printLog(const JobResult& result, const std::filesystem::path& source, const std::filesystem::path& destination);
|
||||
private:
|
||||
std::shared_ptr<Settings> settings;
|
||||
std::atomic<uint32_t> busyThreads;
|
||||
std::atomic<uint32_t> maxTasks;
|
||||
std::atomic<uint32_t> completeTasks;
|
||||
@ -45,7 +49,7 @@ private:
|
||||
std::condition_variable loopConditional;
|
||||
std::condition_variable waitConditional;
|
||||
std::vector<std::thread> threads;
|
||||
std::queue<std::pair<std::string, std::string>> jobs;
|
||||
std::queue<std::pair<std::filesystem::path, std::filesystem::path>> jobs;
|
||||
std::function<bool()> boundLoopCondition;
|
||||
std::function<bool()> boundWaitCondition;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user