regex for additional files copying
This commit is contained in:
parent
eb85b71651
commit
03e7f29d84
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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')
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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) {}
|
||||||
|
@ -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;
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user