help page, number of parallel tasts, source and destination now can be set up from config

This commit is contained in:
Blue 2023-10-09 18:47:00 -03:00
parent 0b268a7245
commit e266008e11
Signed by: blue
GPG Key ID: 9B203B252A63EE38
10 changed files with 197 additions and 43 deletions

View File

@ -1,7 +1,8 @@
# Changelog
## MLC 1.3.0 (UNRELEASED)
## MLC 1.3.0 (October 09, 2023)
- Config file to control the process
- First help page
- Program modes concept to implement config print and help
## MLC 1.2.0 (August 11, 2023)

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.5)
project(
mlc
VERSION 1.2.0
VERSION 1.3.0
DESCRIPTION "Media Library Compiler: rips your media library to a lossy compilation"
LANGUAGES CXX
)

View File

@ -7,6 +7,7 @@ This is a program for compilation of your loseless music library to lossy format
- flac
- lame
- jpeg
- taglib
### Building

View File

@ -29,4 +29,5 @@ target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
add_subdirectory(logger)
make_includable(default.conf ${CMAKE_BINARY_DIR}/generated/default.conf)
make_includable(help ${CMAKE_BINARY_DIR}/generated/help)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR})

View File

@ -1,6 +1,7 @@
# This is a default autogenerated MLC config
# The syntax goes like this:
# key value
# Only the first occasion of the valid key value pair is taken to consideration
#
# Use # sign for comments
#
@ -15,9 +16,31 @@
# 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
# Allowed values are: [debug, info, minor, major, warning, error, fatal]
#level info
# Output type
# Allowed values are: mp3 (more comming soon)
# Allowed values are: [mp3] (more comming soon)
#type mp3
# Source collection path
# This is a default path to your music collection source.
# It's usefull to set it when you always encode the same collection
# Leaving this empty (as it is by default) will make you always
# specify source in the command line
#source
# Destingation path
# This is a default path for your encoding destination
# It's usefull to set it when you often encode your collection
# to the same output
# Leaving this empty (as it is by default) will make you always
# specify destination in the command line
#destination
# Parallel tasks
# Defines how many threads are going to be started in parralel
# Allowed values are [0, 1, 2, 3 ...] etc
# If it's set to 0 - amount of threads is going to be
# as high as your processor can effectively handle
#parallel 0

42
src/help Normal file
View File

@ -0,0 +1,42 @@
Usage:
mlc [action] [arguments] [flags]
Actions:
convert - converts music
config - prints default config
help - prints this page
Default action is `convert`, so it can be omitted
Arguments work only for `convert` action
first - collection source
second - collection destination
Flags:
-c (--config) <path>
- sets custom config instead of default one
-h (--help)
- sets action to help, and prints this page
Examples:
`mlc ~/Music comile/latest`
- reads config file from `~/.config/mlc.conf`
- if there was not - creates default that changes nothing
- creates directory `conpile` in the CURRENT directory if it was not created before
- create `latest` directory in `compile` dirrectory if it was not created before
- searches for music in `~/Music` directory
- converts all music to `comile/latest` directory
- copies all other files found in `~/Music` to `comile/latest`
- any file name overlap will be overriden
`mlc config > myConfig.conf`
- prints default config to standard output
- unix operator `>` redirects output to a file `myConfig.conf`
`mlc rip -c myConfig.conf`
- reads file `myConfig.conf` from current directory
- if `myConfig.conf` has a valid `source` entry - uses it and uses `rip` as destination
- if `myConfig.conf` has a valid `destination` entry - uses it and uses `rip` as source
- if both present (don't do this way) - it depends on the order, who comes first - defines behaviour
- the rest is the same from the first example apart from default config

View File

@ -2,6 +2,10 @@
#include "iostream"
static const char* help =
#include "generated/help"
;
void printHelp() {
std::cout << "Sorry, there is no help yet. It will arrive one day... I hope" << std::endl;
std::cout << help << std::endl;
}

View File

@ -4,15 +4,39 @@ static const char* defaultConfig =
#include "generated/default.conf"
;
enum class Flag {
config,
help,
none
};
enum class Option {
level,
type,
source,
destination,
parallel,
_optionsSize
};
using Literals = std::array<std::string_view, 2>;
constexpr std::array<Literals, static_cast<int>(Flag::none)> flags({{
{"-c", "--config"},
{"-h", "--help"}
}});
constexpr std::array<std::string_view, Settings::_actionsSize> actions({
"convert",
"help",
"config"
});
constexpr std::array<std::string_view, Settings::_optionsSize> options({
constexpr std::array<std::string_view, static_cast<int>(Option::_optionsSize)> options({
"level",
"type"
"type",
"source",
"destination",
"parallel"
});
constexpr std::array<std::string_view, Settings::_typesSize> types({
@ -23,6 +47,25 @@ bool is_space(char ch){
return std::isspace(static_cast<unsigned char>(ch));
}
Flag getFlag(const std::string_view arg) {
for (int i = 0; i < flags.size(); ++i) {
const Literals& lit = flags[i];
unsigned char dist = std::distance(lit.begin(), std::find(lit.begin(), lit.end(), arg));
if (dist < lit.size())
return static_cast<Flag>(i);
}
return Flag::none;
}
Option stringToOption(const std::string& source) {
unsigned char dist = std::distance(options.begin(), std::find(options.begin(), options.end(), source));
if (dist < static_cast<unsigned char>(Option::_optionsSize))
return static_cast<Option>(dist);
return Option::_optionsSize;
}
Settings::Settings(int argc, char ** argv):
arguments(),
action(std::nullopt),
@ -38,16 +81,40 @@ Settings::Settings(int argc, char ** argv):
}
void Settings::parseArguments() {
Flag flag = Flag::none;
for (int i = 0; i < arguments.size(); ++i) {
const std::string_view& arg = arguments[i];
if (i == 0) {
Action act = stringToAction(stripFlags(arg));
Action act = stringToAction(arg);
if (act < _actionsSize) {
action = act;
continue;
}
}
switch (flag) {
case Flag::config:
configPath = arg;
flag = Flag::none;
continue;
case Flag::none:
flag = getFlag(arg);
break;
default:
break;
}
switch (flag) {
case Flag::none:
break;
case Flag::help:
action = help;
flag = Flag::none;
continue;
default:
continue;
}
if (getAction() == convert) {
if (!input.has_value()) {
input = arg;
@ -78,23 +145,23 @@ Settings::Type Settings::getType() const {
std::string Settings::getInput() const {
if (input.has_value())
return input.value();
return resolvePath(input.value());
else
return "";
}
std::string Settings::getOutput() const {
if (output.has_value())
return output.value();
return resolvePath(output.value());
else
return "";
}
std::string Settings::getConfigPath() const {
if (configPath.has_value())
return configPath.value();
return resolvePath(configPath.value());
else
return std::string(getenv("HOME")) + "/.config/mlc.conf";
return resolvePath("~/.config/mlc.conf");
}
bool Settings::isConfigDefault() const {
@ -108,14 +175,11 @@ Logger::Severity Settings::getLogLevel() const {
return Logger::Severity::info;
}
std::string_view Settings::stripFlags(const std::string_view& option) {
if (option[0] == '-') {
if (option[1] == '-')
return option.substr(2);
return option.substr(1);
}
return option;
unsigned int Settings::getThreads() const {
if (threads.has_value())
return threads.value();
else
return 0;
}
void Settings::strip(std::string& line) {
@ -159,26 +223,47 @@ void Settings::readConfigLine(const std::string& line) {
return;
Option option = stringToOption(key);
if (option == _optionsSize)
if (option == Option::_optionsSize)
return;
switch (option) {
case level: {
case Option::level: {
std::string lv;
if (stream >> lv) {
if (!logLevel.has_value() && stream >> lv) {
Logger::Severity level = Logger::stringToSeverity(lv);
if (level < Logger::Severity::_sevetirySize)
logLevel = level;
}
} break;
case type: {
case Option::type: {
std::string lv;
if (stream >> lv) {
if (!outputType.has_value() && stream >> lv) {
Type type = stringToType(lv);
if (type < _typesSize)
outputType = type;
}
} break;
case Option::source: {
std::string path;
if (stream >> path) {
if (!input.has_value()) {
input = path;
} else if (!output.has_value()) {
output = input;
input = path;
}
}
} break;
case Option::destination: {
std::string path;
if (!output.has_value() && stream >> path)
output = path;
} break;
case Option::parallel: {
unsigned int count;
if (!threads.has_value() && stream >> count)
threads = count;
} break;
default:
break;
}
@ -200,14 +285,6 @@ Settings::Action Settings::stringToAction(const std::string_view& source) {
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)
@ -215,3 +292,10 @@ Settings::Type Settings::stringToType(const std::string& source) {
return _typesSize;
}
std::string Settings::resolvePath(const std::string& line) {
if (line.size() > 0 && line[0] == '~')
return getenv("HOME") + line.substr(1);
else
return line;
}

View File

@ -28,12 +28,6 @@ public:
_typesSize
};
enum Option {
level,
type,
_optionsSize
};
Settings(int argc, char **argv);
std::string getInput() const;
@ -43,6 +37,7 @@ public:
Logger::Severity getLogLevel() const;
Type getType() const;
Action getAction() const;
unsigned int getThreads() const;
bool readConfigFile();
void readConfigLine(const std::string& line);
@ -51,14 +46,13 @@ public:
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);
static std::string resolvePath(const std::string& line);
private:
std::vector<std::string_view> arguments;
@ -68,4 +62,5 @@ private:
std::optional<std::string> output;
std::optional<Logger::Severity> logLevel;
std::optional<std::string> configPath;
std::optional<unsigned int> threads;
};

View File

@ -42,8 +42,11 @@ void TaskManager::start() {
if (running)
return;
const uint32_t num_threads = std::thread::hardware_concurrency();
for (uint32_t ii = 0; ii < num_threads; ++ii)
unsigned int amount = settings->getThreads();
if (amount == 0)
amount = std::thread::hardware_concurrency();
for (uint32_t i = 0; i < amount; ++i)
threads.emplace_back(std::thread(&TaskManager::loop, this));
running = true;