help page, number of parallel tasts, source and destination now can be set up from config
This commit is contained in:
parent
0b268a7245
commit
e266008e11
@ -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)
|
||||
|
@ -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
|
||||
)
|
||||
|
@ -7,6 +7,7 @@ This is a program for compilation of your loseless music library to lossy format
|
||||
- flac
|
||||
- lame
|
||||
- jpeg
|
||||
- taglib
|
||||
|
||||
### Building
|
||||
|
||||
|
@ -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})
|
||||
|
@ -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
42
src/help
Normal 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
|
@ -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;
|
||||
}
|
||||
|
140
src/settings.cpp
140
src/settings.cpp
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user