regex for additional files copying

This commit is contained in:
Blue 2023-10-12 22:00:16 -03:00
parent eb85b71651
commit 03e7f29d84
Signed by: blue
GPG Key ID: 9B203B252A63EE38
10 changed files with 146 additions and 45 deletions

View File

@ -1,5 +1,8 @@
# Changelog # Changelog
## MLC 1.3.3 (UNRELEASED)
- Regex to specify non-music files to copy
## MLC 1.3.2 (October 10, 2023) ## MLC 1.3.2 (October 10, 2023)
- A release purely for CI - A release purely for CI

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
project( project(
mlc mlc
VERSION 1.3.212 VERSION 1.3.3
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

@ -1,6 +1,6 @@
# Maintainer: Yury Gubich <blue@macaw.me> # Maintainer: Yury Gubich <blue@macaw.me>
pkgname=mlc pkgname=mlc
pkgver=1.3.212 pkgver=1.3.3
pkgrel=1 pkgrel=1
pkgdesc="Media Library Compiler: rips your media library to a lossy compilation" pkgdesc="Media Library Compiler: rips your media library to a lossy compilation"
arch=('i686' 'x86_64') arch=('i686' 'x86_64')

View File

@ -6,23 +6,16 @@ namespace fs = std::filesystem;
static const std::string flac(".flac"); static const std::string flac(".flac");
Collection::Collection(const std::string& path, TaskManager* tm) : Collection::Collection(const std::filesystem::path& path, TaskManager* tm) :
path(fs::canonical(path)), path(path),
countMusical(0), countMusical(0),
counted(false), counted(false),
taskManager(tm) taskManager(tm)
{} {}
Collection::Collection(const std::filesystem::path& path, TaskManager* tm): Collection::~Collection()
path(fs::canonical(path)),
countMusical(0),
counted(false),
taskManager(tm)
{} {}
Collection::~Collection() {
}
void Collection::list() const { void Collection::list() const {
if (fs::is_regular_file(path)) if (fs::is_regular_file(path))
return; return;
@ -71,13 +64,10 @@ void Collection::convert(const std::string& outPath) {
switch (entry.status().type()) { switch (entry.status().type()) {
case fs::file_type::regular: { case fs::file_type::regular: {
fs::path sourcePath = entry.path(); fs::path sourcePath = entry.path();
fs::path dstPath = out / sourcePath.stem();
if (isMusic(sourcePath)) if (isMusic(sourcePath))
taskManager->queueJob(sourcePath, dstPath); taskManager->queueConvert(sourcePath, out / sourcePath.stem());
else else
fs::copy_file(sourcePath, dstPath, fs::copy_options::overwrite_existing); taskManager->queueCopy(sourcePath, out / sourcePath.filename());
//std::cout << sourcePath << " => " << dstPath << std::endl;
} break; } break;
case fs::file_type::directory: { case fs::file_type::directory: {
fs::path sourcePath = entry.path(); fs::path sourcePath = entry.path();

View File

@ -10,7 +10,6 @@ class TaskManager;
class Collection { class Collection {
public: public:
Collection(const std::string& path, TaskManager* tm = nullptr);
Collection(const std::filesystem::path& path, TaskManager* tm = nullptr); Collection(const std::filesystem::path& path, TaskManager* tm = nullptr);
~Collection(); ~Collection();

View File

@ -44,3 +44,10 @@
# If it's set to 0 - amount of threads is going to be # If it's set to 0 - amount of threads is going to be
# as high as your processor can effectively handle # as high as your processor can effectively handle
#parallel 0 #parallel 0
# Non music files
# MLC copies any non-music file it finds in source directory
# if it matches the following regex
# Allowed value are: [all, none] or regex without any additional syntax,
# for example: filesToCopy cover\.jpe?g
#filesToCopy all

View File

@ -16,6 +16,7 @@ enum class Option {
source, source,
destination, destination,
parallel, parallel,
filesToCopy,
_optionsSize _optionsSize
}; };
@ -36,7 +37,8 @@ constexpr std::array<std::string_view, static_cast<int>(Option::_optionsSize)> o
"type", "type",
"source", "source",
"destination", "destination",
"parallel" "parallel",
"filesToCopy"
}); });
constexpr std::array<std::string_view, Settings::_typesSize> types({ constexpr std::array<std::string_view, Settings::_typesSize> types({
@ -72,7 +74,10 @@ Settings::Settings(int argc, char ** argv):
outputType(std::nullopt), outputType(std::nullopt),
input(std::nullopt), input(std::nullopt),
output(std::nullopt), output(std::nullopt),
logLevel(std::nullopt) logLevel(std::nullopt),
configPath(std::nullopt),
threads(std::nullopt),
nonMusic(std::nullopt)
{ {
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
arguments.push_back(argv[i]); arguments.push_back(argv[i]);
@ -264,6 +269,17 @@ void Settings::readConfigLine(const std::string& line) {
if (!threads.has_value() && stream >> count) if (!threads.has_value() && stream >> count)
threads = count; threads = count;
} break; } break;
case Option::filesToCopy: {
std::string regex;
if (!nonMusic.has_value() && stream >> regex) {
if (regex == "all")
regex = "";
else if (regex == "none")
regex = "a^";
nonMusic = regex;
}
} break;
default: default:
break; break;
} }
@ -299,3 +315,11 @@ std::string Settings::resolvePath(const std::string& line) {
else else
return line; return line;
} }
bool Settings::matchNonMusic(const std::string& fileName) const {
if (nonMusic.has_value())
return std::regex_search(fileName, nonMusic.value());
else
return true;
}

View File

@ -11,6 +11,7 @@
#include <functional> #include <functional>
#include <cctype> #include <cctype>
#include <sstream> #include <sstream>
#include <regex>
#include "logger/logger.h" #include "logger/logger.h"
@ -38,6 +39,7 @@ public:
Type getType() const; Type getType() const;
Action getAction() const; Action getAction() const;
unsigned int getThreads() const; unsigned int getThreads() const;
bool matchNonMusic(const std::string& fileName) const;
bool readConfigFile(); bool readConfigFile();
void readConfigLine(const std::string& line); void readConfigLine(const std::string& line);
@ -63,4 +65,5 @@ private:
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; std::optional<unsigned int> threads;
std::optional<std::regex> nonMusic;
}; };

View File

@ -21,9 +21,9 @@ TaskManager::TaskManager(const std::shared_ptr<Settings>& settings, const std::s
TaskManager::~TaskManager() { TaskManager::~TaskManager() {
} }
void TaskManager::queueJob(const std::filesystem::path& source, const std::filesystem::path& destination) { void TaskManager::queueConvert(const std::filesystem::path& source, const std::filesystem::path& destination) {
std::unique_lock<std::mutex> lock(queueMutex); std::unique_lock<std::mutex> lock(queueMutex);
jobs.emplace(source, destination); jobs.emplace(Job::convert, source, destination);
++maxTasks; ++maxTasks;
logger->setStatusMessage(std::to_string(completeTasks) + "/" + std::to_string(maxTasks)); logger->setStatusMessage(std::to_string(completeTasks) + "/" + std::to_string(maxTasks));
@ -32,6 +32,19 @@ void TaskManager::queueJob(const std::filesystem::path& source, const std::files
loopConditional.notify_one(); loopConditional.notify_one();
} }
void TaskManager::queueCopy(const std::filesystem::path& source, const std::filesystem::path& destination) {
if (!settings->matchNonMusic(source.filename()))
return;
std::unique_lock<std::mutex> lock(queueMutex);
jobs.emplace(Job::copy, source, destination);
++maxTasks;
logger->setStatusMessage(std::to_string(completeTasks) + "/" + std::to_string(maxTasks));
lock.unlock();
loopConditional.notify_one();
}
bool TaskManager::busy() const { bool TaskManager::busy() const {
std::lock_guard lock(queueMutex); std::lock_guard lock(queueMutex);
return !jobs.empty(); return !jobs.empty();
@ -61,28 +74,16 @@ void TaskManager::loop() {
if (terminate) if (terminate)
return; return;
std::pair<std::string, std::string> pair = jobs.front(); Job job = jobs.front();
++busyThreads; ++busyThreads;
jobs.pop(); jobs.pop();
lock.unlock(); lock.unlock();
JobResult result; JobResult result = execute(job);
switch (settings->getType()) {
case Settings::mp3:
result = mp3Job(pair.first, pair.second + ".mp3", settings->getLogLevel());
break;
default:
break;
}
lock.lock(); lock.lock();
++completeTasks; ++completeTasks;
logger->printNested( printResilt(job, result);
result.first ? "Encoding complete but there are messages about it" : "Encoding failed!",
{"Source: \t" + pair.first, "Destination: \t" + pair.second},
result.second,
std::to_string(completeTasks) + "/" + std::to_string(maxTasks)
);
--busyThreads; --busyThreads;
lock.unlock(); lock.unlock();
waitConditional.notify_all(); waitConditional.notify_all();
@ -118,14 +119,72 @@ unsigned int TaskManager::getCompleteTasks() const {
return completeTasks; return completeTasks;
} }
TaskManager::JobResult TaskManager::execute(const Job& job) {
switch (job.type) {
case Job::copy:
return copyJob(job, settings->getLogLevel());
case Job::convert:
switch (settings->getType()) {
case Settings::mp3:
return mp3Job(job, settings->getLogLevel());
default:
break;
}
}
return {false, {
{Logger::Severity::error, "Unknown job type: " + std::to_string(job.type)}
}};
}
void TaskManager::printResilt(const TaskManager::Job& job, const TaskManager::JobResult& result) {
std::string msg;
switch (job.type) {
case Job::copy:
if (result.first)
msg = "File copy complete, but there are messages about it:";
else
msg = "File copy failed!";
break;
case Job::convert:
if (result.first)
msg = "Encoding complete but there are messages about it:";
else
msg = "Encoding failed!";
break;
}
logger->printNested(
msg,
{"Source: \t" + job.source.string(), "Destination: \t" + job.destination.string()},
result.second,
std::to_string(completeTasks) + "/" + std::to_string(maxTasks)
);
}
TaskManager::JobResult TaskManager::mp3Job( TaskManager::JobResult TaskManager::mp3Job(
const std::filesystem::path& source, const TaskManager::Job& job,
const std::filesystem::path& destination,
Logger::Severity logLevel) Logger::Severity logLevel)
{ {
FLACtoMP3 convertor(logLevel); FLACtoMP3 convertor(logLevel);
convertor.setInputFile(source); convertor.setInputFile(job.source);
convertor.setOutputFile(destination); convertor.setOutputFile(job.destination.string() + ".mp3");
bool result = convertor.run(); bool result = convertor.run();
return {result, convertor.getHistory()}; return {result, convertor.getHistory()};
} }
TaskManager::JobResult TaskManager::copyJob(const TaskManager::Job& job, Logger::Severity logLevel) {
(void)(logLevel);
bool success = std::filesystem::copy_file(
job.source,
job.destination,
std::filesystem::copy_options::overwrite_existing
);
return {success, {}};
}
TaskManager::Job::Job(Type type, const std::filesystem::path& source, std::filesystem::path destination):
type(type),
source(source),
destination(destination) {}

View File

@ -18,13 +18,15 @@
#include "logger/printer.h" #include "logger/printer.h"
class TaskManager { class TaskManager {
typedef std::pair<bool, std::list<Logger::Message>> JobResult; using JobResult = std::pair<bool, std::list<Logger::Message>>;
struct Job;
public: public:
TaskManager(const std::shared_ptr<Settings>& settings, const std::shared_ptr<Printer>& logger); TaskManager(const std::shared_ptr<Settings>& settings, const std::shared_ptr<Printer>& logger);
~TaskManager(); ~TaskManager();
void start(); void start();
void queueJob(const std::filesystem::path& source, const std::filesystem::path& destination); void queueConvert(const std::filesystem::path& source, const std::filesystem::path& destination);
void queueCopy(const std::filesystem::path& source, const std::filesystem::path& destination);
void stop(); void stop();
bool busy() const; bool busy() const;
void wait(); void wait();
@ -33,7 +35,11 @@ public:
private: private:
void loop(); void loop();
static JobResult mp3Job(const std::filesystem::path& source, const std::filesystem::path& destination, Logger::Severity logLevel); JobResult execute(const Job& job);
void printResilt(const Job& job, const JobResult& result);
static JobResult mp3Job(const Job& job, Logger::Severity logLevel);
static JobResult copyJob(const Job& job, Logger::Severity logLevel);
private: private:
std::shared_ptr<Settings> settings; std::shared_ptr<Settings> settings;
std::shared_ptr<Printer> logger; std::shared_ptr<Printer> logger;
@ -46,7 +52,17 @@ private:
std::condition_variable loopConditional; std::condition_variable loopConditional;
std::condition_variable waitConditional; std::condition_variable waitConditional;
std::vector<std::thread> threads; std::vector<std::thread> threads;
std::queue<std::pair<std::filesystem::path, std::filesystem::path>> jobs; std::queue<Job> jobs;
}; };
struct TaskManager::Job {
enum Type {
copy,
convert
};
Job(Type type, const std::filesystem::path& source, std::filesystem::path destination);
Type type;
std::filesystem::path source;
std::filesystem::path destination;
};