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
## MLC 1.3.3 (UNRELEASED)
- Regex to specify non-music files to copy
## MLC 1.3.2 (October 10, 2023)
- A release purely for CI

View File

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

View File

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

View File

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

View File

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

View File

@ -44,3 +44,10 @@
# If it's set to 0 - amount of threads is going to be
# as high as your processor can effectively handle
#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,
destination,
parallel,
filesToCopy,
_optionsSize
};
@ -36,7 +37,8 @@ constexpr std::array<std::string_view, static_cast<int>(Option::_optionsSize)> o
"type",
"source",
"destination",
"parallel"
"parallel",
"filesToCopy"
});
constexpr std::array<std::string_view, Settings::_typesSize> types({
@ -72,7 +74,10 @@ Settings::Settings(int argc, char ** argv):
outputType(std::nullopt),
input(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)
arguments.push_back(argv[i]);
@ -264,6 +269,17 @@ void Settings::readConfigLine(const std::string& line) {
if (!threads.has_value() && stream >> count)
threads = count;
} 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:
break;
}
@ -299,3 +315,11 @@ std::string Settings::resolvePath(const std::string& line) {
else
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 <cctype>
#include <sstream>
#include <regex>
#include "logger/logger.h"
@ -38,6 +39,7 @@ public:
Type getType() const;
Action getAction() const;
unsigned int getThreads() const;
bool matchNonMusic(const std::string& fileName) const;
bool readConfigFile();
void readConfigLine(const std::string& line);
@ -63,4 +65,5 @@ private:
std::optional<Logger::Severity> logLevel;
std::optional<std::string> configPath;
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() {
}
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);
jobs.emplace(source, destination);
jobs.emplace(Job::convert, source, destination);
++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();
}
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 {
std::lock_guard lock(queueMutex);
return !jobs.empty();
@ -61,28 +74,16 @@ void TaskManager::loop() {
if (terminate)
return;
std::pair<std::string, std::string> pair = jobs.front();
Job job = jobs.front();
++busyThreads;
jobs.pop();
lock.unlock();
JobResult result;
switch (settings->getType()) {
case Settings::mp3:
result = mp3Job(pair.first, pair.second + ".mp3", settings->getLogLevel());
break;
default:
break;
}
JobResult result = execute(job);
lock.lock();
++completeTasks;
logger->printNested(
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)
);
printResilt(job, result);
--busyThreads;
lock.unlock();
waitConditional.notify_all();
@ -118,14 +119,72 @@ unsigned int TaskManager::getCompleteTasks() const {
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(
const std::filesystem::path& source,
const std::filesystem::path& destination,
const TaskManager::Job& job,
Logger::Severity logLevel)
{
FLACtoMP3 convertor(logLevel);
convertor.setInputFile(source);
convertor.setOutputFile(destination);
convertor.setInputFile(job.source);
convertor.setOutputFile(job.destination.string() + ".mp3");
bool result = convertor.run();
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"
class TaskManager {
typedef std::pair<bool, std::list<Logger::Message>> JobResult;
using JobResult = std::pair<bool, std::list<Logger::Message>>;
struct Job;
public:
TaskManager(const std::shared_ptr<Settings>& settings, const std::shared_ptr<Printer>& logger);
~TaskManager();
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();
bool busy() const;
void wait();
@ -33,7 +35,11 @@ public:
private:
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:
std::shared_ptr<Settings> settings;
std::shared_ptr<Printer> logger;
@ -46,7 +52,17 @@ private:
std::condition_variable loopConditional;
std::condition_variable waitConditional;
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;
};