1
0
forked from blue/mlc

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 untrusted user: blue
GPG Key ID: 9B203B252A63EE38
10 changed files with 197 additions and 43 deletions

View File

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

View File

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

View File

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

View File

@ -29,4 +29,5 @@ target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
add_subdirectory(logger) add_subdirectory(logger)
make_includable(default.conf ${CMAKE_BINARY_DIR}/generated/default.conf) 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}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR})

View File

@ -1,6 +1,7 @@
# This is a default autogenerated MLC config # This is a default autogenerated MLC config
# The syntax goes like this: # The syntax goes like this:
# key value # key value
# Only the first occasion of the valid key value pair is taken to consideration
# #
# Use # sign for comments # Use # sign for comments
# #
@ -15,9 +16,31 @@
# if you wish to override ~/.config/mlc.conf file # if you wish to override ~/.config/mlc.conf file
# Log level, regulates minimal message severity to be printed # 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 #level info
# Output type # Output type
# Allowed values are: mp3 (more comming soon) # Allowed values are: [mp3] (more comming soon)
#type mp3 #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" #include "iostream"
static const char* help =
#include "generated/help"
;
void printHelp() { 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" #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({ constexpr std::array<std::string_view, Settings::_actionsSize> actions({
"convert", "convert",
"help", "help",
"config" "config"
}); });
constexpr std::array<std::string_view, Settings::_optionsSize> options({ constexpr std::array<std::string_view, static_cast<int>(Option::_optionsSize)> options({
"level", "level",
"type" "type",
"source",
"destination",
"parallel"
}); });
constexpr std::array<std::string_view, Settings::_typesSize> types({ 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)); 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): Settings::Settings(int argc, char ** argv):
arguments(), arguments(),
action(std::nullopt), action(std::nullopt),
@ -38,16 +81,40 @@ Settings::Settings(int argc, char ** argv):
} }
void Settings::parseArguments() { void Settings::parseArguments() {
Flag flag = Flag::none;
for (int i = 0; i < arguments.size(); ++i) { for (int i = 0; i < arguments.size(); ++i) {
const std::string_view& arg = arguments[i]; const std::string_view& arg = arguments[i];
if (i == 0) { if (i == 0) {
Action act = stringToAction(stripFlags(arg)); Action act = stringToAction(arg);
if (act < _actionsSize) { if (act < _actionsSize) {
action = act; action = act;
continue; 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 (getAction() == convert) {
if (!input.has_value()) { if (!input.has_value()) {
input = arg; input = arg;
@ -78,23 +145,23 @@ Settings::Type Settings::getType() const {
std::string Settings::getInput() const { std::string Settings::getInput() const {
if (input.has_value()) if (input.has_value())
return input.value(); return resolvePath(input.value());
else else
return ""; return "";
} }
std::string Settings::getOutput() const { std::string Settings::getOutput() const {
if (output.has_value()) if (output.has_value())
return output.value(); return resolvePath(output.value());
else else
return ""; return "";
} }
std::string Settings::getConfigPath() const { std::string Settings::getConfigPath() const {
if (configPath.has_value()) if (configPath.has_value())
return configPath.value(); return resolvePath(configPath.value());
else else
return std::string(getenv("HOME")) + "/.config/mlc.conf"; return resolvePath("~/.config/mlc.conf");
} }
bool Settings::isConfigDefault() const { bool Settings::isConfigDefault() const {
@ -108,14 +175,11 @@ Logger::Severity Settings::getLogLevel() const {
return Logger::Severity::info; return Logger::Severity::info;
} }
std::string_view Settings::stripFlags(const std::string_view& option) { unsigned int Settings::getThreads() const {
if (option[0] == '-') { if (threads.has_value())
if (option[1] == '-') return threads.value();
return option.substr(2); else
return 0;
return option.substr(1);
}
return option;
} }
void Settings::strip(std::string& line) { void Settings::strip(std::string& line) {
@ -159,26 +223,47 @@ void Settings::readConfigLine(const std::string& line) {
return; return;
Option option = stringToOption(key); Option option = stringToOption(key);
if (option == _optionsSize) if (option == Option::_optionsSize)
return; return;
switch (option) { switch (option) {
case level: { case Option::level: {
std::string lv; std::string lv;
if (stream >> lv) { if (!logLevel.has_value() && stream >> lv) {
Logger::Severity level = Logger::stringToSeverity(lv); Logger::Severity level = Logger::stringToSeverity(lv);
if (level < Logger::Severity::_sevetirySize) if (level < Logger::Severity::_sevetirySize)
logLevel = level; logLevel = level;
} }
} break; } break;
case type: { case Option::type: {
std::string lv; std::string lv;
if (stream >> lv) { if (!outputType.has_value() && stream >> lv) {
Type type = stringToType(lv); Type type = stringToType(lv);
if (type < _typesSize) if (type < _typesSize)
outputType = type; outputType = type;
} }
} break; } 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: default:
break; break;
} }
@ -200,14 +285,6 @@ Settings::Action Settings::stringToAction(const std::string_view& source) {
return _actionsSize; 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) { Settings::Type Settings::stringToType(const std::string& source) {
unsigned char dist = std::distance(types.begin(), std::find(types.begin(), types.end(), source)); unsigned char dist = std::distance(types.begin(), std::find(types.begin(), types.end(), source));
if (dist < _typesSize) if (dist < _typesSize)
@ -215,3 +292,10 @@ Settings::Type Settings::stringToType(const std::string& source) {
return _typesSize; 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 _typesSize
}; };
enum Option {
level,
type,
_optionsSize
};
Settings(int argc, char **argv); Settings(int argc, char **argv);
std::string getInput() const; std::string getInput() const;
@ -43,6 +37,7 @@ public:
Logger::Severity getLogLevel() const; Logger::Severity getLogLevel() const;
Type getType() const; Type getType() const;
Action getAction() const; Action getAction() const;
unsigned int getThreads() const;
bool readConfigFile(); bool readConfigFile();
void readConfigLine(const std::string& line); void readConfigLine(const std::string& line);
@ -51,14 +46,13 @@ public:
static Action stringToAction(const std::string& source); static Action stringToAction(const std::string& source);
static Action stringToAction(const std::string_view& source); static Action stringToAction(const std::string_view& source);
static Type stringToType(const std::string& source); static Type stringToType(const std::string& source);
static Option stringToOption(const std::string& source);
private: private:
void parseArguments(); void parseArguments();
static std::string_view stripFlags(const std::string_view& option);
static void strip(std::string& line); static void strip(std::string& line);
static void stripComment(std::string& line); static void stripComment(std::string& line);
static std::string resolvePath(const std::string& line);
private: private:
std::vector<std::string_view> arguments; std::vector<std::string_view> arguments;
@ -68,4 +62,5 @@ private:
std::optional<std::string> output; std::optional<std::string> output;
std::optional<Logger::Severity> logLevel; std::optional<Logger::Severity> logLevel;
std::optional<std::string> configPath; std::optional<std::string> configPath;
std::optional<unsigned int> threads;
}; };

View File

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