From eb85b716514d1e946b7e2167c455c4158e620348 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 11 Oct 2023 17:11:23 -0300 Subject: [PATCH 1/7] CI, try 12 --- .gitea/workflows/release.yml | 2 +- CMakeLists.txt | 2 +- packaging/Archlinux/PKGBUILD | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index dbf6560..8e6d811 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -22,7 +22,7 @@ jobs: echo "${{ secrets.DEPLOY_TO_AUR_PRIVATE_KEY }}" > key chmod 600 key GIT_SSH_COMMAND="ssh -i key -o 'IdentitiesOnly yes' -o 'StrictHostKeyChecking no'" git clone ssh://aur@aur.archlinux.org/mlc.git aur - chmod 777 -R aur/.SRCINFO + chmod 777 -R aur cd aur git config user.name ${{ secrets.DEPLOY_TO_AUR_USER_NAME }} git config user.email ${{ secrets.DEPLOY_TO_AUR_EMAIL }} diff --git a/CMakeLists.txt b/CMakeLists.txt index c7b9191..18af7d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5) project( mlc - VERSION 1.3.211 + VERSION 1.3.212 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 0794ce8..cf2b341 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Yury Gubich pkgname=mlc -pkgver=1.3.211 +pkgver=1.3.212 pkgrel=1 pkgdesc="Media Library Compiler: rips your media library to a lossy compilation" arch=('i686' 'x86_64') From 03e7f29d8470031cb8f174081665760a2486853a Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 12 Oct 2023 22:00:16 -0300 Subject: [PATCH 2/7] regex for additional files copying --- CHANGELOG.md | 3 ++ CMakeLists.txt | 2 +- packaging/Archlinux/PKGBUILD | 2 +- src/collection.cpp | 20 ++----- src/collection.h | 1 - src/default.conf | 7 +++ src/settings.cpp | 28 +++++++++- src/settings.h | 3 ++ src/taskmanager.cpp | 101 +++++++++++++++++++++++++++-------- src/taskmanager.h | 24 +++++++-- 10 files changed, 146 insertions(+), 45 deletions(-) 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; +}; From 5c3a4a592e3be9dabe234cceccc9a3e2580e2363 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 13 Oct 2023 16:10:08 -0300 Subject: [PATCH 3/7] encoding settings --- CHANGELOG.md | 3 ++- src/default.conf | 45 +++++++++++++++++++++++++++++++++++++++ src/flactomp3.cpp | 34 ++++++++++++++++++++++++----- src/flactomp3.h | 2 ++ src/settings.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++-- src/settings.h | 6 ++++++ src/taskmanager.cpp | 22 +++++++++---------- src/taskmanager.h | 6 +++--- 8 files changed, 148 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d7b62e..baa5c37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ # Changelog -## MLC 1.3.3 (UNRELEASED) +## MLC 1.3.3 (October 13, 2023) - Regex to specify non-music files to copy +- Encoding settings (VBR/CBR, encoding quality, output quality) ## MLC 1.3.2 (October 10, 2023) - A release purely for CI diff --git a/src/default.conf b/src/default.conf index 7add1f7..68f329a 100644 --- a/src/default.conf +++ b/src/default.conf @@ -51,3 +51,48 @@ # Allowed value are: [all, none] or regex without any additional syntax, # for example: filesToCopy cover\.jpe?g #filesToCopy all + +# Encoding quality +# Sets up encoding quality (NOT OUTPUT QUALITY) +# The higher quality the slower the encoding process +# 0 is the highest quality and slowest process +# 9 is the lowest quality and the fastest process fastest +# Allowed values are: [0, 1, 2, ... 9] +#encodingQuality 0 + +# Output quality +# Sets up output quality +# The higher quality the less information is lonst in compression +# 0 is the highest possible quality for selected mode and type and results in the biggest file +# 9 is the lowest quality but results in the smalest file +# Allowed values are [0, 1, 2, ... 9] +# For the constant bitrate modes (CBR) the following table is valid +# Quality | MP3 | +# --------+-----+-- +# 0 | 320 | +# --------+-----+-- +# 1 | 288 | +# --------+-----+-- +# 2 | 256 | +# --------+-----+-- +# 3 | 224 | +# --------+-----+-- +# 4 | 192 | +# --------+-----+-- +# 5 | 160 | +# --------+-----+-- +# 6 | 128 | +# --------+-----+-- +# 7 | 96 | +# --------+-----+-- +# 8 | 64 | +# --------+-----+-- +# 9 | 32 | +#outputQuality 0 + +# Variable bitrate +# Switches on or off variable bitrate +# VBR files are usually smaller, but not supped to be worse +# in terms of quality. VBR files might be a bit more tricky for the player +# Allowedvalues are: [true, false] +#vbr true diff --git a/src/flactomp3.cpp b/src/flactomp3.cpp index 5b01c1c..2f7d67c 100644 --- a/src/flactomp3.cpp +++ b/src/flactomp3.cpp @@ -12,6 +12,18 @@ constexpr std::string_view jpeg ("image/jpeg"); const std::map textIdentificationReplacements({ {"PUBLISHER", "TPUB"} }); +constexpr std::array bitrates({ + 320, + 288, + 256, + 224, + 192, + 160, + 128, + 96, + 64, + 32 +}); FLACtoMP3::FLACtoMP3(Logger::Severity severity, uint8_t size) : logger(severity), @@ -46,7 +58,7 @@ bool FLACtoMP3::run() { if (pcmCounter > 0) flush(); - int nwrite = lame_encode_flush(encoder, outputBuffer, pcmSize * 2); + int nwrite = lame_encode_flush(encoder, outputBuffer, outputBufferSize); fwrite((char*)outputBuffer, nwrite, 1, output); @@ -98,9 +110,21 @@ void FLACtoMP3::setOutputFile(const std::string& path) { outPath = path; - lame_set_VBR(encoder, vbr_default); - lame_set_VBR_quality(encoder, 0); - lame_set_quality(encoder, 0); +} + +void FLACtoMP3::setParameters(unsigned char encodingQuality, unsigned char outputQuality, bool vbr) { + if (vbr) { + logger.info("Encoding to VBR with quality " + std::to_string(outputQuality)); + lame_set_VBR(encoder, vbr_default); + lame_set_VBR_quality(encoder, outputQuality); + } else { + int bitrate = bitrates[outputQuality]; + logger.info("Encoding to CBR " + std::to_string(bitrate)); + lame_set_VBR(encoder, vbr_off); + lame_set_brate(encoder, bitrate); + } + + lame_set_quality(encoder, encodingQuality); } bool FLACtoMP3::initializeOutput() { @@ -306,7 +330,7 @@ bool FLACtoMP3::flush() { nwrite = lame_encode_buffer_interleaved( encoder, pcm, - pcmCounter, + pcmCounter / 2, outputBuffer, outputBufferSize ); diff --git a/src/flactomp3.h b/src/flactomp3.h index 6ebefc6..fba27db 100644 --- a/src/flactomp3.h +++ b/src/flactomp3.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "logger/accumulator.h" @@ -19,6 +20,7 @@ public: void setInputFile(const std::string& path); void setOutputFile(const std::string& path); + void setParameters(unsigned char encodingQuality, unsigned char outputQuality, bool vbr); bool run(); std::list getHistory() const; diff --git a/src/settings.cpp b/src/settings.cpp index 889aef4..0dabf3b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -17,6 +17,9 @@ enum class Option { destination, parallel, filesToCopy, + encodingQuality, + outputQuality, + vbr, _optionsSize }; @@ -38,13 +41,19 @@ constexpr std::array(Option::_optionsSize)> o "source", "destination", "parallel", - "filesToCopy" + "filesToCopy", + "encodingQuality", + "outputQuality", + "vbr" }); constexpr std::array types({ "mp3" }); +constexpr unsigned int maxQuality = 9; +constexpr unsigned int minQuality = 0; + bool is_space(char ch){ return std::isspace(static_cast(ch)); } @@ -77,7 +86,10 @@ Settings::Settings(int argc, char ** argv): logLevel(std::nullopt), configPath(std::nullopt), threads(std::nullopt), - nonMusic(std::nullopt) + nonMusic(std::nullopt), + encodingQuality(std::nullopt), + outputQuality(std::nullopt), + vbr(std::nullopt) { for (int i = 1; i < argc; ++i) arguments.push_back(argv[i]); @@ -187,6 +199,27 @@ unsigned int Settings::getThreads() const { return 0; } +unsigned char Settings::getOutputQuality() const { + if (outputQuality.has_value()) + return outputQuality.value(); + else + return minQuality; //actually, it means max possible quality +} + +unsigned char Settings::getEncodingQuality() const { + if (encodingQuality.has_value()) + return encodingQuality.value(); + else + return minQuality; //actually, it means max possible quality +} + +bool Settings::getVBR() const { + if (vbr.has_value()) + return vbr.value(); + else + return true; +} + void Settings::strip(std::string& line) { line.erase(line.begin(), std::find_if(line.begin(), line.end(), std::not_fn(is_space))); line.erase(std::find_if(line.rbegin(), line.rend(), std::not_fn(is_space)).base(), line.end()); @@ -280,6 +313,21 @@ void Settings::readConfigLine(const std::string& line) { nonMusic = regex; } } break; + case Option::outputQuality: { + unsigned int value; + if (!outputQuality.has_value() && stream >> value) + outputQuality = std::clamp(value, minQuality, maxQuality); + } break; + case Option::encodingQuality: { + unsigned int value; + if (!encodingQuality.has_value() && stream >> value) + encodingQuality = std::clamp(value, minQuality, maxQuality); + } break; + case Option::vbr: { + bool value; + if (!vbr.has_value() && stream >> std::boolalpha >> value) + vbr = value; + } break; default: break; } diff --git a/src/settings.h b/src/settings.h index 7c33935..d13b7f8 100644 --- a/src/settings.h +++ b/src/settings.h @@ -40,6 +40,9 @@ public: Action getAction() const; unsigned int getThreads() const; bool matchNonMusic(const std::string& fileName) const; + unsigned char getEncodingQuality() const; + unsigned char getOutputQuality() const; + bool getVBR() const; bool readConfigFile(); void readConfigLine(const std::string& line); @@ -66,4 +69,7 @@ private: std::optional configPath; std::optional threads; std::optional nonMusic; + std::optional encodingQuality; + std::optional outputQuality; + std::optional vbr; }; diff --git a/src/taskmanager.cpp b/src/taskmanager.cpp index 5569040..81cd97f 100644 --- a/src/taskmanager.cpp +++ b/src/taskmanager.cpp @@ -119,14 +119,15 @@ unsigned int TaskManager::getCompleteTasks() const { return completeTasks; } -TaskManager::JobResult TaskManager::execute(const Job& job) { +TaskManager::JobResult TaskManager::execute(Job& job) { switch (job.type) { case Job::copy: - return copyJob(job, settings->getLogLevel()); + return copyJob(job, settings); case Job::convert: switch (settings->getType()) { case Settings::mp3: - return mp3Job(job, settings->getLogLevel()); + job.destination.replace_extension("mp3"); + return mp3Job(job, settings); default: break; } @@ -162,19 +163,18 @@ void TaskManager::printResilt(const TaskManager::Job& job, const TaskManager::Jo ); } -TaskManager::JobResult TaskManager::mp3Job( - const TaskManager::Job& job, - Logger::Severity logLevel) -{ - FLACtoMP3 convertor(logLevel); +TaskManager::JobResult TaskManager::mp3Job(const TaskManager::Job& job, const std::shared_ptr& settings) { + FLACtoMP3 convertor(settings->getLogLevel()); convertor.setInputFile(job.source); - convertor.setOutputFile(job.destination.string() + ".mp3"); + convertor.setOutputFile(job.destination); + convertor.setParameters(settings->getEncodingQuality(), settings->getOutputQuality(), settings->getVBR()); bool result = convertor.run(); + return {result, convertor.getHistory()}; } -TaskManager::JobResult TaskManager::copyJob(const TaskManager::Job& job, Logger::Severity logLevel) { - (void)(logLevel); +TaskManager::JobResult TaskManager::copyJob(const TaskManager::Job& job, const std::shared_ptr& settings) { + (void)(settings); bool success = std::filesystem::copy_file( job.source, job.destination, diff --git a/src/taskmanager.h b/src/taskmanager.h index 25f8ddb..022c016 100644 --- a/src/taskmanager.h +++ b/src/taskmanager.h @@ -35,10 +35,10 @@ public: private: void loop(); - JobResult execute(const Job& job); + JobResult execute(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); + static JobResult mp3Job(const Job& job, const std::shared_ptr& settings); + static JobResult copyJob(const Job& job, const std::shared_ptr& settings); private: std::shared_ptr settings; From 2cce5f52f022ab81b43a4a2b2e516217fba55929 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 20 Oct 2023 17:39:27 -0300 Subject: [PATCH 4/7] build scenario changes --- CMakeLists.txt | 16 +++++++--------- cmake/FindFLAC.cmake | 23 +++++++++++++++++++++++ cmake/FindLAME.cmake | 36 +++++++++++++++++++++--------------- cmake/FindTAGLIB.cmake | 24 ++++++++++++++++++++++++ src/flactomp3.h | 2 +- 5 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 cmake/FindFLAC.cmake create mode 100644 cmake/FindTAGLIB.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 9529f49..315042f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,23 +26,21 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") -find_package(PkgConfig REQUIRED) find_package(FLAC REQUIRED) find_package(JPEG REQUIRED) +find_package(LAME REQUIRED) +find_package(TAGLIB REQUIRED) -pkg_check_modules(LAME REQUIRED IMPORTED_TARGET lame) -pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib) - -add_executable(mlc) +add_executable(${PROJECT_NAME}) target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS}) add_subdirectory(src) -target_link_libraries(mlc +target_link_libraries(${PROJECT_NAME} FLAC::FLAC - PkgConfig::LAME + LAME::LAME JPEG::JPEG - PkgConfig::TAGLIB + TAGLIB::TAGLIB ) -install(TARGETS mlc RUNTIME DESTINATION bin) +install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) diff --git a/cmake/FindFLAC.cmake b/cmake/FindFLAC.cmake new file mode 100644 index 0000000..bfae68f --- /dev/null +++ b/cmake/FindFLAC.cmake @@ -0,0 +1,23 @@ +find_path(FLAC_INCLUDE_DIR FLAC/stream_decoder.h) +find_library(FLAC_LIBRARIES FLAC NAMES flac) + +if(FLAC_INCLUDE_DIR AND FLAC_LIBRARIES) + set(FLAC_FOUND TRUE) +endif() + +if(FLAC_FOUND) + add_library(FLAC::FLAC SHARED IMPORTED) + set_target_properties(FLAC::FLAC PROPERTIES + IMPORTED_LOCATION "${FLAC_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${FLAC_INCLUDE_DIR}/FLAC" + INTERFACE_LINK_LIBRARIES "${FLAC_LIBRARIES}" + ) + if (NOT FLAC_FIND_QUIETLY) + message(STATUS "Found FLAC includes: ${FLAC_INCLUDE_DIR}/FLAC") + message(STATUS "Found FLAC library: ${FLAC_LIBRARIES}") + endif () +else() + if (FLAC_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find FLAC development files") + endif () +endif() diff --git a/cmake/FindLAME.cmake b/cmake/FindLAME.cmake index e19bd79..4aefca1 100644 --- a/cmake/FindLAME.cmake +++ b/cmake/FindLAME.cmake @@ -1,20 +1,26 @@ #copied from here, thank you #https://github.com/sipwise/sems/blob/master/cmake/FindLame.cmake -FIND_PATH(LAME_INCLUDE_DIR lame/lame.h) -FIND_LIBRARY(LAME_LIBRARIES NAMES mp3lame) +find_path(LAME_INCLUDE_DIR lame/lame.h) +find_library(LAME_LIBRARIES lame NAMES mp3lame) -IF(LAME_INCLUDE_DIR AND LAME_LIBRARIES) - SET(LAME_FOUND TRUE) -ENDIF(LAME_INCLUDE_DIR AND LAME_LIBRARIES) +if(LAME_INCLUDE_DIR AND LAME_LIBRARIES) + set(LAME_FOUND TRUE) +endif(LAME_INCLUDE_DIR AND LAME_LIBRARIES) -IF(LAME_FOUND) - IF (NOT Lame_FIND_QUIETLY) - MESSAGE(STATUS "Found lame includes: ${LAME_INCLUDE_DIR}/lame/lame.h") - MESSAGE(STATUS "Found lame library: ${LAME_LIBRARIES}") - ENDIF (NOT Lame_FIND_QUIETLY) -ELSE(LAME_FOUND) - IF (Lame_FIND_REQUIRED) - MESSAGE(FATAL_ERROR "Could NOT find lame development files") - ENDIF (Lame_FIND_REQUIRED) -ENDIF(LAME_FOUND) +if(LAME_FOUND) + add_library(LAME::LAME SHARED IMPORTED) + set_target_properties(LAME::LAME PROPERTIES + IMPORTED_LOCATION "${LAME_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${LAME_INCLUDE_DIR}/lame" + INTERFACE_LINK_LIBRARIES "${LAME_LIBRARIES}" + ) + if (NOT Lame_FIND_QUIETLY) + message(STATUS "Found lame includes: ${LAME_INCLUDE_DIR}/lame") + message(STATUS "Found lame library: ${LAME_LIBRARIES}") + endif (NOT Lame_FIND_QUIETLY) +else(LAME_FOUND) + if (Lame_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find lame development files") + endif (Lame_FIND_REQUIRED) +endif(LAME_FOUND) diff --git a/cmake/FindTAGLIB.cmake b/cmake/FindTAGLIB.cmake new file mode 100644 index 0000000..b4e2b2b --- /dev/null +++ b/cmake/FindTAGLIB.cmake @@ -0,0 +1,24 @@ +find_path(TAGLIB_INCLUDE_DIR taglib/id3v2tag.h) +find_library(TAGLIB_LIBRARIES taglib NAMES TAGLIB tag) + +if(TAGLIB_INCLUDE_DIR AND TAGLIB_LIBRARIES) + set(TAGLIB_FOUND TRUE) +endif() + +if(TAGLIB_FOUND) + add_library(TAGLIB::TAGLIB SHARED IMPORTED) + set_target_properties(TAGLIB::TAGLIB PROPERTIES + IMPORTED_LOCATION "${TAGLIB_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${FLAC_INCLUDE_DIR}/taglib" + INTERFACE_LINK_LIBRARIES "${TAGLIB_LIBRARIES}" + ) + if (NOT TAGLIB_FIND_QUIETLY) + message(STATUS "Found TAGLIB includes: ${FLAC_INCLUDE_DIR}/taglib") + message(STATUS "Found TAGLIB library: ${TAGLIB_LIBRARIES}") + endif () +else() + if (TAGLIB_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find TAGLIB development files") + endif () +endif() + diff --git a/src/flactomp3.h b/src/flactomp3.h index fba27db..0bf1a8a 100644 --- a/src/flactomp3.h +++ b/src/flactomp3.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include #include From 3971a5b662bed7391a5dd2d2750d72f807f55b9d Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 20 Oct 2023 18:12:26 -0300 Subject: [PATCH 5/7] pthreads for compatibility --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 315042f..cd5a172 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,11 +25,13 @@ message("Compilation options: " ${COMPILE_OPTIONS}) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") +set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(FLAC REQUIRED) find_package(JPEG REQUIRED) find_package(LAME REQUIRED) find_package(TAGLIB REQUIRED) +find_package(Threads REQUIRED) add_executable(${PROJECT_NAME}) target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS}) @@ -41,6 +43,7 @@ target_link_libraries(${PROJECT_NAME} LAME::LAME JPEG::JPEG TAGLIB::TAGLIB + Threads::Threads ) install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) From ceab08a26dee947d86ca406c2b385950372ce3ce Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 6 Jul 2024 13:53:26 -0300 Subject: [PATCH 6/7] Typo fixes, readme updates --- CHANGELOG.md | 5 +++- CMakeLists.txt | 4 +-- README.md | 50 +++++++++++++++++++++++++++++++----- cmake/FindTAGLIB.cmake | 4 +-- packaging/Archlinux/PKGBUILD | 2 +- src/default.conf | 20 +++++++-------- src/help | 16 ++++++------ src/logger/logger.cpp | 6 ++--- src/logger/logger.h | 2 +- src/logger/printer.cpp | 4 +-- src/settings.cpp | 6 ++--- 11 files changed, 80 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index baa5c37..9fbc7bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## MLC 1.3.4 (UNRELEASED) +- Build fixes + ## MLC 1.3.3 (October 13, 2023) - Regex to specify non-music files to copy - Encoding settings (VBR/CBR, encoding quality, output quality) @@ -23,7 +26,7 @@ ## MLC 1.1.0 (July 23, 2023) - New logging system -- Artist, Album artist, Album and Title are now utf16 encoded, should fix broten titles +- Artist, Album artist, Album and Title are now utf16 encoded, should fix broken titles - BPM tag is now rounded, as it supposed to be by spec - Lyrics is not set now for USLT tag for unsychronized lyrics is not supported in LAME - Date bug fix, it was MMDD instead of standard DDMM diff --git a/CMakeLists.txt b/CMakeLists.txt index cd5a172..0c5aa2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.5) project( mlc - VERSION 1.3.3 - DESCRIPTION "Media Library Compiler: rips your media library to a lossy compilation" + VERSION 1.3.4 + DESCRIPTION "Media Library Compiler: converts your media library to a lossy compilation" LANGUAGES CXX ) cmake_policy(SET CMP0076 NEW) diff --git a/README.md b/README.md index 8d3f250..4beaa8b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # MLC - Music Library Compiler -This is a program for compilation of your loseless music library to lossy formats +[![AUR version](https://img.shields.io/aur/version/mlc?style=flat-square)](https://aur.archlinux.org/packages/mlc/) + +This is a program to compile your local lossless music library to lossy formats ### Prerequisites @@ -11,7 +13,7 @@ This is a program for compilation of your loseless music library to lossy format ### Building -``` +```sh $ git clone https://git.macaw.me/blue/mlc $ cd mlc $ mkdir build @@ -22,12 +24,48 @@ $ cmake --build . ### Usage +Just to compile lossless library to lossy you can use this command +assuming you are in the same directory you have just built `MLC`: + ``` -./mlc path/to/loseless/library path/to/store/lossy/library +./mlc path/to/lossless/library path/to/store/lossy/library ``` -For now the program is very primitive, it only works to convert `.flac` files (it trusts the suffix) to `.mp3` VBR best possible quality slowest possible conversion algorithm. +There are more ways to use `MLC`, please refer to help for more options: -MLC keeps file structure, copies all the non `.flac` files, tries to adapt some `.flac` (vorbis) tags to id3v1 and id3v2 of destination `.mp3` file. +```sh +$ ./mlc help +# or +$ ./mlc --help +# +# or +$ ./mlc -h +``` -For now it's siglethread, no interrupt controll, not even sure converted files are valid, no exotic cases handled, no conversion options can be passed. May be will improve in the futer. +`MLC` has a way to configure conversion process, it will generate global for you user config on the first launch. +It will be located in `~/.config/mlc.conf` and `MLC` will notify you when it's generated. + +You can also make local configs for each directory you launch mlc from. + +To output the default config run the following, assuming you are in the same directory you have just built `MLC`: + +```sh +$ ./mlc config +``` + +To use non default config run the following, assuming you are in the same directory you have just built `MLC`: + +```sh +$ ./mlc path/to/lossless/library path/to/store/lossy/library -c path/to/config/file +``` + +### About + +For now the program is very primitive, it only works to convert `.flac` files (it trusts the suffix) to `.mp3`. +I'm planing to add more lossy formats first then, may be, more lossless. + +`MLC` keeps file structure, copies all the non `.flac` files, tries to adapt some `.flac` (vorbis) tags to id3v1 and id3v2 of destination `.mp3` file. +You can set up output format and quality and which kind of files should it skip in the config. + +By default `MLC` runs on as many threads as your system can provide to speed up conversion process. +You can set up correct amount of threads in the config. diff --git a/cmake/FindTAGLIB.cmake b/cmake/FindTAGLIB.cmake index b4e2b2b..1ec7b1c 100644 --- a/cmake/FindTAGLIB.cmake +++ b/cmake/FindTAGLIB.cmake @@ -9,11 +9,11 @@ if(TAGLIB_FOUND) add_library(TAGLIB::TAGLIB SHARED IMPORTED) set_target_properties(TAGLIB::TAGLIB PROPERTIES IMPORTED_LOCATION "${TAGLIB_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${FLAC_INCLUDE_DIR}/taglib" + INTERFACE_INCLUDE_DIRECTORIES "${TAGLIB_INCLUDE_DIR}/taglib" INTERFACE_LINK_LIBRARIES "${TAGLIB_LIBRARIES}" ) if (NOT TAGLIB_FIND_QUIETLY) - message(STATUS "Found TAGLIB includes: ${FLAC_INCLUDE_DIR}/taglib") + message(STATUS "Found TAGLIB includes: ${TAGLIB_INCLUDE_DIR}/taglib") message(STATUS "Found TAGLIB library: ${TAGLIB_LIBRARIES}") endif () else() diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index 266d3ae..93a9aef 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,6 +1,6 @@ # Maintainer: Yury Gubich pkgname=mlc -pkgver=1.3.3 +pkgver=1.3.4 pkgrel=1 pkgdesc="Media Library Compiler: rips your media library to a lossy compilation" arch=('i686' 'x86_64') diff --git a/src/default.conf b/src/default.conf index 68f329a..d7ef46d 100644 --- a/src/default.conf +++ b/src/default.conf @@ -5,12 +5,12 @@ # # Use # sign for comments # -# All printed comented out values are default values +# All printed commented out values are default values # # You should have one of this files as ~/.config/mlc.conf, # if you have started mlc at least once. # This is going to be used every time you launch mlc, -# so, edit it if you wish to change default behaviour. +# so, edit it if you wish to change default behavior. # # Alternatively, you can launch `mlc -c customConfig.conf` # if you wish to override ~/.config/mlc.conf file @@ -20,26 +20,26 @@ #level info # Output type -# Allowed values are: [mp3] (more comming soon) +# Allowed values are: [mp3] (more coming soon) #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 +# It's useful 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 +# Destination path # This is a default path for your encoding destination -# It's usefull to set it when you often encode your collection +# It's useful 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 +# Defines how many threads are going to be started in parallel # 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 @@ -62,9 +62,9 @@ # Output quality # Sets up output quality -# The higher quality the less information is lonst in compression +# The higher quality the less information is lost in compression # 0 is the highest possible quality for selected mode and type and results in the biggest file -# 9 is the lowest quality but results in the smalest file +# 9 is the lowest quality but results in the smallest file # Allowed values are [0, 1, 2, ... 9] # For the constant bitrate modes (CBR) the following table is valid # Quality | MP3 | @@ -94,5 +94,5 @@ # Switches on or off variable bitrate # VBR files are usually smaller, but not supped to be worse # in terms of quality. VBR files might be a bit more tricky for the player -# Allowedvalues are: [true, false] +# Allowed values are: [true, false] #vbr true diff --git a/src/help b/src/help index 34356f5..622aba1 100644 --- a/src/help +++ b/src/help @@ -20,23 +20,23 @@ Flags: - sets action to help, and prints this page Examples: - `mlc ~/Music comile/latest` + `mlc ~/Music compile/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 + - creates directory `compile` in the CURRENT directory if it was not created before + - create `latest` directory in `compile` directory 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 + - converts all music to `compile/latest` directory + - copies all other files found in `~/Music` to `compile/latest` + - any file name overlap will be overridden `mlc config > myConfig.conf` - prints default config to standard output - unix operator `>` redirects output to a file `myConfig.conf` - `mlc rip -c myConfig.conf` + `mlc convert -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 + - if both present (don't do this way) - it depends on the order, who comes first - defines behavior - the rest is the same from the first example apart from default config diff --git a/src/logger/logger.cpp b/src/logger/logger.cpp index 9be0ef6..35780ee 100644 --- a/src/logger/logger.cpp +++ b/src/logger/logger.cpp @@ -4,7 +4,7 @@ #include #include -constexpr std::array(Logger::Severity::_sevetirySize)> levels({ +constexpr std::array(Logger::Severity::_severitySize)> levels({ "debug", "info", "minor", @@ -46,8 +46,8 @@ void Logger::fatal(const std::string& comment, bool colored) const { Logger::Severity Logger::stringToSeverity(const std::string& line) { unsigned char dist = std::distance(levels.begin(), std::find(levels.begin(), levels.end(), line)); - if (dist < static_cast(Severity::_sevetirySize)) + if (dist < static_cast(Severity::_severitySize)) return static_cast(dist); - return Severity::_sevetirySize; + return Severity::_severitySize; } diff --git a/src/logger/logger.h b/src/logger/logger.h index 65b00c7..6608f50 100644 --- a/src/logger/logger.h +++ b/src/logger/logger.h @@ -13,7 +13,7 @@ public: warning, error, fatal, - _sevetirySize + _severitySize }; using Message = std::pair; diff --git a/src/logger/printer.cpp b/src/logger/printer.cpp index 8c1f49c..5040fa2 100644 --- a/src/logger/printer.cpp +++ b/src/logger/printer.cpp @@ -4,7 +4,7 @@ #include #include -constexpr const std::array(Logger::Severity::_sevetirySize)> logSettings({ +constexpr const std::array(Logger::Severity::_severitySize)> logSettings({ /*debug*/ "\e[90m", /*info*/ "\e[32m", /*minor*/ "\e[34m", @@ -14,7 +14,7 @@ constexpr const std::array(Logger::Severity:: /*fatal*/ "\e[91m" }); -constexpr const std::array(Logger::Severity::_sevetirySize)> logHeaders({ +constexpr const std::array(Logger::Severity::_severitySize)> logHeaders({ /*debug*/ "DEBUG: ", /*info*/ "INFO: ", /*minor*/ "MINOR: ", diff --git a/src/settings.cpp b/src/settings.cpp index 0dabf3b..39be0a7 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -203,14 +203,14 @@ unsigned char Settings::getOutputQuality() const { if (outputQuality.has_value()) return outputQuality.value(); else - return minQuality; //actually, it means max possible quality + return minQuality; //it means max possible quality, min is for min enum value } unsigned char Settings::getEncodingQuality() const { if (encodingQuality.has_value()) return encodingQuality.value(); else - return minQuality; //actually, it means max possible quality + return minQuality; //it means max possible quality, min is for min enum value } bool Settings::getVBR() const { @@ -269,7 +269,7 @@ void Settings::readConfigLine(const std::string& line) { std::string lv; if (!logLevel.has_value() && stream >> lv) { Logger::Severity level = Logger::stringToSeverity(lv); - if (level < Logger::Severity::_sevetirySize) + if (level < Logger::Severity::_severitySize) logLevel = level; } } break; From bf88e05a13659e929d054f381e8a1cc6ea82db68 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 30 Mar 2025 22:07:20 +0300 Subject: [PATCH 7/7] Space treatment, a bit more memory safe handling, exclude option, typo fixes --- CHANGELOG.md | 7 ++-- README.md | 34 +++++++++--------- src/collection.cpp | 21 +++++++----- src/collection.h | 7 ++-- src/default.conf | 10 +++++- src/main.cpp | 10 +++--- src/settings.cpp | 84 ++++++++++++++++++++++++++------------------- src/settings.h | 2 ++ src/taskmanager.cpp | 7 ++-- src/taskmanager.h | 2 +- 10 files changed, 111 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fbc7bd..acae7f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,10 @@ # Changelog -## MLC 1.3.4 (UNRELEASED) +## MLC 1.3.4 (March 30, 2025) - Build fixes +- Source and Destination paths now can contain spaces +- Regex to copy non music files can have spaces +- A feature to define a regex to exclude paths from rendering ## MLC 1.3.3 (October 13, 2023) - Regex to specify non-music files to copy @@ -28,7 +31,7 @@ - New logging system - Artist, Album artist, Album and Title are now utf16 encoded, should fix broken titles - BPM tag is now rounded, as it supposed to be by spec -- Lyrics is not set now for USLT tag for unsychronized lyrics is not supported in LAME +- Lyrics is not set now for USLT tag for unsynchronized lyrics is not supported in LAME - Date bug fix, it was MMDD instead of standard DDMM ## MLC 1.0.1 (July 21, 2023) diff --git a/README.md b/README.md index 4beaa8b..c08255a 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,13 @@ This is a program to compile your local lossless music library to lossy formats ### Building -```sh -$ git clone https://git.macaw.me/blue/mlc -$ cd mlc -$ mkdir build -$ cd build -$ cmake .. -$ cmake --build . +```shell +git clone https://git.macaw.me/blue/mlc +cd mlc +mkdir build +cd build +cmake .. +cmake --build . ``` ### Usage @@ -27,19 +27,19 @@ $ cmake --build . Just to compile lossless library to lossy you can use this command assuming you are in the same directory you have just built `MLC`: -``` +```shell ./mlc path/to/lossless/library path/to/store/lossy/library ``` There are more ways to use `MLC`, please refer to help for more options: -```sh -$ ./mlc help +```shell +./mlc help # or -$ ./mlc --help +./mlc --help # # or -$ ./mlc -h +./mlc -h ``` `MLC` has a way to configure conversion process, it will generate global for you user config on the first launch. @@ -49,14 +49,16 @@ You can also make local configs for each directory you launch mlc from. To output the default config run the following, assuming you are in the same directory you have just built `MLC`: -```sh -$ ./mlc config +```shell +./mlc config #to print +#or +./mlc config > config.conf #to save to config.conf in current directory ``` To use non default config run the following, assuming you are in the same directory you have just built `MLC`: -```sh -$ ./mlc path/to/lossless/library path/to/store/lossy/library -c path/to/config/file +```shell +./mlc path/to/lossless/library path/to/store/lossy/library -c path/to/config/file ``` ### About diff --git a/src/collection.cpp b/src/collection.cpp index dad6991..c47e8ed 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -6,11 +6,12 @@ namespace fs = std::filesystem; static const std::string flac(".flac"); -Collection::Collection(const std::filesystem::path& path, TaskManager* tm) : +Collection::Collection(const std::filesystem::path& path, const std::shared_ptr& tm, const std::shared_ptr& st): path(path), countMusical(0), counted(false), - taskManager(tm) + taskManager(tm), + settings(st) {} Collection::~Collection() @@ -33,13 +34,16 @@ uint32_t Collection::countMusicFiles() const { ++countMusical; } else if (fs::is_directory(path)) { for (const fs::directory_entry& entry : fs::directory_iterator(path)) { + if (settings->isExcluded(entry.path())) + continue; + switch (entry.status().type()) { case fs::file_type::regular: if (isMusic(entry.path())) ++countMusical; break; case fs::file_type::directory: { - Collection collection(entry.path()); + Collection collection(entry.path(), taskManager, settings); countMusical += collection.countMusicFiles(); } break; default: @@ -53,25 +57,24 @@ uint32_t Collection::countMusicFiles() const { } void Collection::convert(const std::string& outPath) { - if (taskManager == nullptr) - throw 6; - fs::path out = fs::absolute(outPath); fs::create_directories(out); out = fs::canonical(outPath); for (const fs::directory_entry& entry : fs::directory_iterator(path)) { + fs::path sourcePath = entry.path(); + if (settings->isExcluded(sourcePath)) + continue; + switch (entry.status().type()) { case fs::file_type::regular: { - fs::path sourcePath = entry.path(); if (isMusic(sourcePath)) taskManager->queueConvert(sourcePath, out / sourcePath.stem()); else taskManager->queueCopy(sourcePath, out / sourcePath.filename()); } break; case fs::file_type::directory: { - fs::path sourcePath = entry.path(); - Collection collection(sourcePath, taskManager); + Collection collection(sourcePath, taskManager, settings); fs::path::iterator itr = sourcePath.end(); --itr; collection.convert(std::string(out / *itr)); diff --git a/src/collection.h b/src/collection.h index 1a3c254..bb107fb 100644 --- a/src/collection.h +++ b/src/collection.h @@ -3,14 +3,16 @@ #include #include #include +#include +#include "settings.h" #include "flactomp3.h" class TaskManager; class Collection { public: - Collection(const std::filesystem::path& path, TaskManager* tm = nullptr); + Collection(const std::filesystem::path& path, const std::shared_ptr& tm, const std::shared_ptr& st); ~Collection(); void list() const; @@ -24,6 +26,7 @@ private: std::filesystem::path path; mutable uint32_t countMusical; mutable bool counted; - TaskManager* taskManager; + std::shared_ptr taskManager; + std::shared_ptr settings; }; diff --git a/src/default.conf b/src/default.conf index d7ef46d..97f6a56 100644 --- a/src/default.conf +++ b/src/default.conf @@ -92,7 +92,15 @@ # Variable bitrate # Switches on or off variable bitrate -# VBR files are usually smaller, but not supped to be worse +# VBR files are usually smaller, but not supposed to be worse # in terms of quality. VBR files might be a bit more tricky for the player # Allowed values are: [true, false] #vbr true + +# Exclude +# MLC renders any music file it finds in source directory +# UNLESS its path matches the following regex +# Allowed value is the regex without any additional syntax, +# for example: exclude [Ss]hamefull?\s[Ss]ong[Ss] +# If you don't want to exclude anything leave this option blank +#exclude \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index dedcae5..50bded3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -57,16 +57,16 @@ int main(int argc, char **argv) { } logger->setSeverity(settings->getLogLevel()); - TaskManager taskManager(settings, logger); - taskManager.start(); + std::shared_ptr taskManager = std::make_shared(settings, logger); + taskManager->start(); std::chrono::time_point start = std::chrono::system_clock::now(); - Collection collection(input, &taskManager); + Collection collection(input, taskManager, settings); collection.convert(output); - taskManager.wait(); + taskManager->wait(); std::cout << std::endl; - taskManager.stop(); + taskManager->stop(); std::chrono::time_point end = std::chrono::system_clock::now(); std::chrono::duration seconds = end - start; diff --git a/src/settings.cpp b/src/settings.cpp index 39be0a7..9ecb3c0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -17,6 +17,7 @@ enum class Option { destination, parallel, filesToCopy, + exclude, encodingQuality, outputQuality, vbr, @@ -42,6 +43,7 @@ constexpr std::array(Option::_optionsSize)> o "destination", "parallel", "filesToCopy", + "exclude", "encodingQuality", "outputQuality", "vbr" @@ -264,69 +266,74 @@ void Settings::readConfigLine(const std::string& line) { if (option == Option::_optionsSize) return; + std::string value; + std::getline(stream >> std::ws, value); + strip(value); + if (value.empty()) + return; + switch (option) { case Option::level: { std::string lv; - if (!logLevel.has_value() && stream >> lv) { + if (!logLevel.has_value() && std::istringstream(value) >> lv) { Logger::Severity level = Logger::stringToSeverity(lv); if (level < Logger::Severity::_severitySize) logLevel = level; } } break; case Option::type: { - std::string lv; - if (!outputType.has_value() && stream >> lv) { - Type type = stringToType(lv); + std::string tp; + if (!outputType.has_value() && std::istringstream(value) >> tp) { + Type type = stringToType(tp); if (type < _typesSize) outputType = type; } } 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; - } + if (!input.has_value()) { + input = value; + } else if (!output.has_value()) { + output = input; + input = value; } } break; case Option::destination: { - std::string path; - if (!output.has_value() && stream >> path) - output = path; + if (!output.has_value()) + output = value; } break; case Option::parallel: { unsigned int count; - if (!threads.has_value() && stream >> count) + if (!threads.has_value() && std::istringstream(value) >> 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^"; + if (!nonMusic.has_value()) { + if (value == "all") + value = ""; + else if (value == "none") + value = "a^"; - nonMusic = regex; + nonMusic = value; } } break; + case Option::exclude: { + if (!excluded.has_value()) + excluded = value; + } break; case Option::outputQuality: { - unsigned int value; - if (!outputQuality.has_value() && stream >> value) - outputQuality = std::clamp(value, minQuality, maxQuality); + unsigned int quality; + if (!outputQuality.has_value() && std::istringstream(value) >> quality) + outputQuality = std::clamp(quality, minQuality, maxQuality); } break; case Option::encodingQuality: { - unsigned int value; - if (!encodingQuality.has_value() && stream >> value) - encodingQuality = std::clamp(value, minQuality, maxQuality); + unsigned int quality; + if (!encodingQuality.has_value() && std::istringstream(value) >> quality) + encodingQuality = std::clamp(quality, minQuality, maxQuality); } break; case Option::vbr: { - bool value; - if (!vbr.has_value() && stream >> std::boolalpha >> value) - vbr = value; + bool vb; + if (!vbr.has_value() && std::istringstream(value) >> std::boolalpha >> vb) + vbr = vb; } break; default: break; @@ -365,9 +372,16 @@ std::string Settings::resolvePath(const std::string& line) { } bool Settings::matchNonMusic(const std::string& fileName) const { - if (nonMusic.has_value()) - return std::regex_search(fileName, nonMusic.value()); - else + if (!nonMusic.has_value()) return true; + + return std::regex_search(fileName, nonMusic.value()); +} + +bool Settings::isExcluded(const std::string& path) const { + if (!excluded.has_value()) + return false; + + return std::regex_search(path, excluded.value()); } diff --git a/src/settings.h b/src/settings.h index d13b7f8..de9fddc 100644 --- a/src/settings.h +++ b/src/settings.h @@ -40,6 +40,7 @@ public: Action getAction() const; unsigned int getThreads() const; bool matchNonMusic(const std::string& fileName) const; + bool isExcluded(const std::string& path) const; unsigned char getEncodingQuality() const; unsigned char getOutputQuality() const; bool getVBR() const; @@ -69,6 +70,7 @@ private: std::optional configPath; std::optional threads; std::optional nonMusic; + std::optional excluded; std::optional encodingQuality; std::optional outputQuality; std::optional vbr; diff --git a/src/taskmanager.cpp b/src/taskmanager.cpp index 81cd97f..03693d4 100644 --- a/src/taskmanager.cpp +++ b/src/taskmanager.cpp @@ -22,6 +22,9 @@ TaskManager::~TaskManager() { } void TaskManager::queueConvert(const std::filesystem::path& source, const std::filesystem::path& destination) { + if (settings->isExcluded(source)) + return; + std::unique_lock lock(queueMutex); jobs.emplace(Job::convert, source, destination); @@ -83,7 +86,7 @@ void TaskManager::loop() { lock.lock(); ++completeTasks; - printResilt(job, result); + printResult(job, result); --busyThreads; lock.unlock(); waitConditional.notify_all(); @@ -138,7 +141,7 @@ TaskManager::JobResult TaskManager::execute(Job& job) { }}; } -void TaskManager::printResilt(const TaskManager::Job& job, const TaskManager::JobResult& result) { +void TaskManager::printResult(const TaskManager::Job& job, const TaskManager::JobResult& result) { std::string msg; switch (job.type) { case Job::copy: diff --git a/src/taskmanager.h b/src/taskmanager.h index 022c016..4955ece 100644 --- a/src/taskmanager.h +++ b/src/taskmanager.h @@ -36,7 +36,7 @@ public: private: void loop(); JobResult execute(Job& job); - void printResilt(const Job& job, const JobResult& result); + void printResult(const Job& job, const JobResult& result); static JobResult mp3Job(const Job& job, const std::shared_ptr& settings); static JobResult copyJob(const Job& job, const std::shared_ptr& settings);