Compare commits
4 Commits
Author | SHA1 | Date |
---|---|---|
Blue | 3971a5b662 | |
Blue | 2cce5f52f0 | |
Blue | 5c3a4a592e | |
Blue | 03e7f29d84 |
|
@ -1,5 +1,9 @@
|
|||
# Changelog
|
||||
|
||||
## 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
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
@ -25,24 +25,25 @@ 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(PkgConfig REQUIRED)
|
||||
find_package(FLAC REQUIRED)
|
||||
find_package(JPEG REQUIRED)
|
||||
find_package(LAME REQUIRED)
|
||||
find_package(TAGLIB REQUIRED)
|
||||
find_package(Threads 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
|
||||
Threads::Threads
|
||||
)
|
||||
|
||||
install(TARGETS mlc RUNTIME DESTINATION bin)
|
||||
install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin)
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
@ -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')
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -44,3 +44,55 @@
|
|||
# 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
|
||||
|
||||
# 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
|
||||
|
|
|
@ -12,6 +12,18 @@ constexpr std::string_view jpeg ("image/jpeg");
|
|||
const std::map<std::string, std::string> textIdentificationReplacements({
|
||||
{"PUBLISHER", "TPUB"}
|
||||
});
|
||||
constexpr std::array<int, 10> 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
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <FLAC/stream_decoder.h>
|
||||
#include <stream_decoder.h>
|
||||
#include <lame.h>
|
||||
#include <jpeglib.h>
|
||||
#include <id3v2tag.h>
|
||||
|
@ -8,6 +8,7 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <stdio.h>
|
||||
|
||||
#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<Logger::Message> getHistory() const;
|
||||
|
|
|
@ -16,6 +16,10 @@ enum class Option {
|
|||
source,
|
||||
destination,
|
||||
parallel,
|
||||
filesToCopy,
|
||||
encodingQuality,
|
||||
outputQuality,
|
||||
vbr,
|
||||
_optionsSize
|
||||
};
|
||||
|
||||
|
@ -36,13 +40,20 @@ constexpr std::array<std::string_view, static_cast<int>(Option::_optionsSize)> o
|
|||
"type",
|
||||
"source",
|
||||
"destination",
|
||||
"parallel"
|
||||
"parallel",
|
||||
"filesToCopy",
|
||||
"encodingQuality",
|
||||
"outputQuality",
|
||||
"vbr"
|
||||
});
|
||||
|
||||
constexpr std::array<std::string_view, Settings::_typesSize> types({
|
||||
"mp3"
|
||||
});
|
||||
|
||||
constexpr unsigned int maxQuality = 9;
|
||||
constexpr unsigned int minQuality = 0;
|
||||
|
||||
bool is_space(char ch){
|
||||
return std::isspace(static_cast<unsigned char>(ch));
|
||||
}
|
||||
|
@ -72,7 +83,13 @@ 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),
|
||||
encodingQuality(std::nullopt),
|
||||
outputQuality(std::nullopt),
|
||||
vbr(std::nullopt)
|
||||
{
|
||||
for (int i = 1; i < argc; ++i)
|
||||
arguments.push_back(argv[i]);
|
||||
|
@ -182,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());
|
||||
|
@ -264,6 +302,32 @@ 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;
|
||||
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;
|
||||
}
|
||||
|
@ -299,3 +363,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <functional>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
|
||||
#include "logger/logger.h"
|
||||
|
||||
|
@ -38,6 +39,10 @@ public:
|
|||
Type getType() const;
|
||||
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);
|
||||
|
@ -63,4 +68,8 @@ private:
|
|||
std::optional<Logger::Severity> logLevel;
|
||||
std::optional<std::string> configPath;
|
||||
std::optional<unsigned int> threads;
|
||||
std::optional<std::regex> nonMusic;
|
||||
std::optional<unsigned char> encodingQuality;
|
||||
std::optional<unsigned char> outputQuality;
|
||||
std::optional<bool> vbr;
|
||||
};
|
||||
|
|
|
@ -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::mp3Job(
|
||||
const std::filesystem::path& source,
|
||||
const std::filesystem::path& destination,
|
||||
Logger::Severity logLevel)
|
||||
{
|
||||
FLACtoMP3 convertor(logLevel);
|
||||
convertor.setInputFile(source);
|
||||
convertor.setOutputFile(destination);
|
||||
TaskManager::JobResult TaskManager::execute(Job& job) {
|
||||
switch (job.type) {
|
||||
case Job::copy:
|
||||
return copyJob(job, settings);
|
||||
case Job::convert:
|
||||
switch (settings->getType()) {
|
||||
case Settings::mp3:
|
||||
job.destination.replace_extension("mp3");
|
||||
return mp3Job(job, settings);
|
||||
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 TaskManager::Job& job, const std::shared_ptr<Settings>& settings) {
|
||||
FLACtoMP3 convertor(settings->getLogLevel());
|
||||
convertor.setInputFile(job.source);
|
||||
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, const std::shared_ptr<Settings>& settings) {
|
||||
(void)(settings);
|
||||
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"
|
||||
|
||||
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(Job& job);
|
||||
void printResilt(const Job& job, const JobResult& result);
|
||||
static JobResult mp3Job(const Job& job, const std::shared_ptr<Settings>& settings);
|
||||
static JobResult copyJob(const Job& job, const std::shared_ptr<Settings>& settings);
|
||||
|
||||
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;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue