diff --git a/CHANGELOG.md b/CHANGELOG.md index e4ec26a..5d7b62e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 18af7d8..9529f49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index cf2b341..266d3ae 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Yury Gubich 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') diff --git a/src/collection.cpp b/src/collection.cpp index c71b257..dad6991 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -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(); diff --git a/src/collection.h b/src/collection.h index b531f4b..1a3c254 100644 --- a/src/collection.h +++ b/src/collection.h @@ -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(); diff --git a/src/default.conf b/src/default.conf index 67fd2ef..7add1f7 100644 --- a/src/default.conf +++ b/src/default.conf @@ -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 diff --git a/src/settings.cpp b/src/settings.cpp index 53eaa0a..889aef4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -16,6 +16,7 @@ enum class Option { source, destination, parallel, + filesToCopy, _optionsSize }; @@ -36,7 +37,8 @@ constexpr std::array(Option::_optionsSize)> o "type", "source", "destination", - "parallel" + "parallel", + "filesToCopy" }); constexpr std::array 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; +} + diff --git a/src/settings.h b/src/settings.h index 4a763c8..7c33935 100644 --- a/src/settings.h +++ b/src/settings.h @@ -11,6 +11,7 @@ #include #include #include +#include #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 logLevel; std::optional configPath; std::optional threads; + std::optional nonMusic; }; diff --git a/src/taskmanager.cpp b/src/taskmanager.cpp index b0750d0..5569040 100644 --- a/src/taskmanager.cpp +++ b/src/taskmanager.cpp @@ -21,9 +21,9 @@ TaskManager::TaskManager(const std::shared_ptr& 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 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 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 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) {} diff --git a/src/taskmanager.h b/src/taskmanager.h index 7100135..25f8ddb 100644 --- a/src/taskmanager.h +++ b/src/taskmanager.h @@ -18,13 +18,15 @@ #include "logger/printer.h" class TaskManager { - typedef std::pair> JobResult; + using JobResult = std::pair>; + struct Job; public: TaskManager(const std::shared_ptr& settings, const std::shared_ptr& 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; std::shared_ptr logger; @@ -46,7 +52,17 @@ private: std::condition_variable loopConditional; std::condition_variable waitConditional; std::vector threads; - std::queue> jobs; + std::queue 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; +};