changed logging concept, reworked task manager

This commit is contained in:
Blue 2023-10-08 20:29:40 -03:00
parent 6c7356598a
commit 0b268a7245
Signed by: blue
GPG Key ID: 9B203B252A63EE38
19 changed files with 497 additions and 260 deletions

View File

@ -1,5 +1,9 @@
# Changelog # Changelog
## MLC 1.3.0 (UNRELEASED)
- Config file to control the process
- Program modes concept to implement config print and help
## MLC 1.2.0 (August 11, 2023) ## MLC 1.2.0 (August 11, 2023)
- Better way of handling tags using TagLib - Better way of handling tags using TagLib

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.5)
project( project(
mlc mlc
VERSION 1.2.0 VERSION 1.2.0

View File

@ -12,7 +12,6 @@ set(SOURCES
flactomp3.cpp flactomp3.cpp
collection.cpp collection.cpp
taskmanager.cpp taskmanager.cpp
loggable.cpp
settings.cpp settings.cpp
) )
@ -22,11 +21,12 @@ set(HEADERS
flactomp3.h flactomp3.h
collection.h collection.h
taskmanager.h taskmanager.h
loggable.h
settings.h settings.h
) )
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})
add_subdirectory(logger)
make_includable(default.conf ${CMAKE_BINARY_DIR}/generated/default.conf) make_includable(default.conf ${CMAKE_BINARY_DIR}/generated/default.conf)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR}) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR})

View File

@ -13,8 +13,8 @@ const std::map<std::string, std::string> textIdentificationReplacements({
{"PUBLISHER", "TPUB"} {"PUBLISHER", "TPUB"}
}); });
FLACtoMP3::FLACtoMP3(Severity severity, uint8_t size) : FLACtoMP3::FLACtoMP3(Logger::Severity severity, uint8_t size) :
Loggable(severity), logger(severity),
inPath(), inPath(),
outPath(), outPath(),
decoder(FLAC__stream_decoder_new()), decoder(FLAC__stream_decoder_new()),
@ -72,7 +72,7 @@ bool FLACtoMP3::run() {
float MBytes = (float)fileSize / 1024 / 1024; float MBytes = (float)fileSize / 1024 / 1024;
std::string strMBytes = std::to_string(MBytes); std::string strMBytes = std::to_string(MBytes);
strMBytes = strMBytes.substr(0, strMBytes.find(".") + 3) + " MiB"; strMBytes = strMBytes.substr(0, strMBytes.find(".") + 3) + " MiB";
log(info, "resulting file size: " + strMBytes); logger.info("resulting file size: " + strMBytes);
} }
return ok; return ok;
@ -110,13 +110,13 @@ bool FLACtoMP3::initializeOutput() {
output = fopen(outPath.c_str(), "w+b"); output = fopen(outPath.c_str(), "w+b");
if (output == 0) { if (output == 0) {
output = nullptr; output = nullptr;
log(fatal, "Error opening file " + outPath); logger.fatal("Error opening file " + outPath);
return false; return false;
} }
int ret = lame_init_params(encoder); int ret = lame_init_params(encoder);
if (ret < 0) { if (ret < 0) {
log(fatal, "Error initializing LAME parameters. Code = " + std::to_string(ret)); logger.fatal("Error initializing LAME parameters. Code = " + std::to_string(ret));
fclose(output); fclose(output);
output = nullptr; output = nullptr;
return false; return false;
@ -143,9 +143,9 @@ void FLACtoMP3::processInfo(const FLAC__StreamMetadata_StreamInfo& info) {
lame_set_in_samplerate(encoder, info.sample_rate); lame_set_in_samplerate(encoder, info.sample_rate);
lame_set_num_channels(encoder, info.channels); lame_set_num_channels(encoder, info.channels);
flacMaxBlockSize = info.max_blocksize; flacMaxBlockSize = info.max_blocksize;
log(Loggable::info, "sample rate: " + std::to_string(info.sample_rate)); logger.info("sample rate: " + std::to_string(info.sample_rate));
log(Loggable::info, "channels: " + std::to_string(info.channels)); logger.info("channels: " + std::to_string(info.channels));
log(Loggable::info, "bits per sample: " + std::to_string(info.bits_per_sample)); logger.info("bits per sample: " + std::to_string(info.bits_per_sample));
} }
void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) { void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) {
@ -156,7 +156,7 @@ void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) {
std::string_view comm((const char*)entry.entry); std::string_view comm((const char*)entry.entry);
std::string_view::size_type ePos = comm.find("="); std::string_view::size_type ePos = comm.find("=");
if (ePos == std::string_view::npos) { if (ePos == std::string_view::npos) {
log(warning, "couldn't understand tag (" + std::string(comm) + "), symbol '=' is missing, skipping"); logger.warn("couldn't understand tag (" + std::string(comm) + "), symbol '=' is missing, skipping");
continue; continue;
} }
std::string key(comm.substr(0, ePos)); std::string key(comm.substr(0, ePos));
@ -174,17 +174,17 @@ void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) {
TagLib::ID3v2::TextIdentificationFrame* frame = new TagLib::ID3v2::TextIdentificationFrame(itr->second.c_str()); TagLib::ID3v2::TextIdentificationFrame* frame = new TagLib::ID3v2::TextIdentificationFrame(itr->second.c_str());
frame->setText(value); frame->setText(value);
customFrames.push_back(frame); customFrames.push_back(frame);
log(debug, "tag \"" + key + "\" was remapped to \"" + itr->second + "\""); logger.debug("tag \"" + key + "\" was remapped to \"" + itr->second + "\"");
} else { } else {
success = props.insert(key, TagLib::String(value, TagLib::String::UTF8)); success = props.insert(key, TagLib::String(value, TagLib::String::UTF8));
} }
if (!success) if (!success)
log(warning, "couldn't understand tag (" + key + "), skipping"); logger.warn("couldn't understand tag (" + key + "), skipping");
} }
TagLib::StringList unsupported = props.unsupportedData(); TagLib::StringList unsupported = props.unsupportedData();
for (const TagLib::String& key : unsupported) for (const TagLib::String& key : unsupported)
log(minor, "tag \"" + key.to8Bit() + "\", is not supported, probably won't display well"); logger.minor("tag \"" + key.to8Bit() + "\", is not supported, probably won't display well");
id3v2tag.setProperties(props); id3v2tag.setProperties(props);
@ -195,13 +195,13 @@ void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) {
void FLACtoMP3::processPicture(const FLAC__StreamMetadata_Picture& picture) { void FLACtoMP3::processPicture(const FLAC__StreamMetadata_Picture& picture) {
if (downscaleAlbumArt && picture.data_length > LAME_MAXALBUMART) { if (downscaleAlbumArt && picture.data_length > LAME_MAXALBUMART) {
log(info, "embeded album art is too big (" + std::to_string(picture.data_length) + " bytes), rescaling"); logger.info("embeded album art is too big (" + std::to_string(picture.data_length) + " bytes), rescaling");
log(debug, "mime type is " + std::string(picture.mime_type)); logger.debug("mime type is " + std::string(picture.mime_type));
if (picture.mime_type == jpeg) { if (picture.mime_type == jpeg) {
if (scaleJPEG(picture)) if (scaleJPEG(picture))
log(debug, "successfully rescaled album art"); logger.debug("successfully rescaled album art");
else else
log(warning, "failed to rescale album art"); logger.warn("failed to rescale album art");
} }
} else { } else {
//auch, sorry for copying so much, but I haven't found a way around it yet //auch, sorry for copying so much, but I haven't found a way around it yet
@ -220,7 +220,7 @@ bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
int rc = jpeg_read_header(&dinfo, TRUE); int rc = jpeg_read_header(&dinfo, TRUE);
if (rc != 1) { if (rc != 1) {
log(Loggable::error, "error reading jpeg header"); logger.error("error reading jpeg header");
return false; return false;
} }
TagLib::ByteVector vector (picture.data_length + 1024 * 4); //I allocate a little bit more not to corrupt someone else's the memory TagLib::ByteVector vector (picture.data_length + 1024 * 4); //I allocate a little bit more not to corrupt someone else's the memory
@ -251,7 +251,7 @@ bool FLACtoMP3::scaleJPEG(const FLAC__StreamMetadata_Picture& picture) {
while (dinfo.output_scanline < dinfo.output_height) { while (dinfo.output_scanline < dinfo.output_height) {
if (mem_size + rowSize > vector.size()) { if (mem_size + rowSize > vector.size()) {
vector.resize(vector.size() + rowSize); vector.resize(vector.size() + rowSize);
log(Loggable::major, "allocated memory for resising the image wasn't enougth, resising"); logger.major("allocated memory for resising the image wasn't enougth, resising");
} }
jpeg_read_scanlines(&dinfo, &row, 1); jpeg_read_scanlines(&dinfo, &row, 1);
jpeg_write_scanlines(&cinfo, &row, 1); jpeg_write_scanlines(&cinfo, &row, 1);
@ -297,11 +297,11 @@ bool FLACtoMP3::flush() {
outputBufferSize outputBufferSize
); );
while (nwrite == -1) { //-1 is returned when there was not enough space in the given buffer while (nwrite == -1) { //-1 is returned when there was not enough space in the given buffer
log(major, std::to_string(outputBufferSize) + " bytes in the output buffer wasn't enough");; logger.major(std::to_string(outputBufferSize) + " bytes in the output buffer wasn't enough");;
outputBufferSize = outputBufferSize * 2; outputBufferSize = outputBufferSize * 2;
delete[] outputBuffer; delete[] outputBuffer;
outputBuffer = new uint8_t[outputBufferSize]; outputBuffer = new uint8_t[outputBufferSize];
log(major, "allocating " + std::to_string(outputBufferSize) + " bytes"); logger.major("allocating " + std::to_string(outputBufferSize) + " bytes");
nwrite = lame_encode_buffer_interleaved( nwrite = lame_encode_buffer_interleaved(
encoder, encoder,
@ -318,10 +318,10 @@ bool FLACtoMP3::flush() {
return actuallyWritten == 1; return actuallyWritten == 1;
} else { } else {
if (nwrite == 0) { if (nwrite == 0) {
log(minor, "encoding flush encoded 0 bytes, skipping write"); logger.minor("encoding flush encoded 0 bytes, skipping write");
return true; return true;
} else { } else {
log(fatal, "encoding flush failed. Code = : " + std::to_string(nwrite)); logger.fatal("encoding flush failed. Code = : " + std::to_string(nwrite));
return false; return false;
} }
} }
@ -359,15 +359,15 @@ FLAC__StreamDecoderWriteStatus FLACtoMP3::write(
// } // }
FLACtoMP3* self = static_cast<FLACtoMP3*>(client_data); FLACtoMP3* self = static_cast<FLACtoMP3*>(client_data);
if (frame->header.channels != 2) { if (frame->header.channels != 2) {
self->log(fatal, "ERROR: This frame contains " + std::to_string(frame->header.channels) + " channels (should be 2)"); self->logger.fatal("ERROR: This frame contains " + std::to_string(frame->header.channels) + " channels (should be 2)");
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
} }
if (buffer[0] == NULL) { if (buffer[0] == NULL) {
self->log(fatal, "ERROR: buffer [0] is NULL"); self->logger.fatal("ERROR: buffer [0] is NULL");
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
} }
if (buffer[1] == NULL) { if (buffer[1] == NULL) {
self->log(fatal, "ERROR: buffer [1] is NULL"); self->logger.fatal("ERROR: buffer [1] is NULL");
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
} }
@ -383,7 +383,7 @@ void FLACtoMP3::error(const FLAC__StreamDecoder* decoder, FLAC__StreamDecoderErr
(void)decoder; (void)decoder;
FLACtoMP3* self = static_cast<FLACtoMP3*>(client_data); FLACtoMP3* self = static_cast<FLACtoMP3*>(client_data);
std::string errText(FLAC__StreamDecoderErrorStatusString[status]); std::string errText(FLAC__StreamDecoderErrorStatusString[status]);
self->log(Loggable::error, "Got error callback: " + errText); self->logger.error("Got error callback: " + errText);
} }
void FLACtoMP3::attachPictureFrame(const FLAC__StreamMetadata_Picture& picture, const TagLib::ByteVector& bytes) { void FLACtoMP3::attachPictureFrame(const FLAC__StreamMetadata_Picture& picture, const TagLib::ByteVector& bytes) {
@ -395,91 +395,91 @@ void FLACtoMP3::attachPictureFrame(const FLAC__StreamMetadata_Picture& picture,
switch (picture.type) { switch (picture.type) {
case FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER: case FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Other); frame->setType(TagLib::ID3v2::AttachedPictureFrame::Other);
log(info, "attached picture is described as \"other\""); logger.info("attached picture is described as \"other\"");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD: case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::FileIcon); frame->setType(TagLib::ID3v2::AttachedPictureFrame::FileIcon);
log(info, "attached picture is a standard file icon"); logger.info("attached picture is a standard file icon");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON: case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::OtherFileIcon); frame->setType(TagLib::ID3v2::AttachedPictureFrame::OtherFileIcon);
log(info, "attached picture is apparently not so standard file icon..."); logger.info("attached picture is apparently not so standard file icon...");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER: case FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover); frame->setType(TagLib::ID3v2::AttachedPictureFrame::FrontCover);
log(info, "attached picture is a front album cover"); logger.info("attached picture is a front album cover");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER: case FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::BackCover); frame->setType(TagLib::ID3v2::AttachedPictureFrame::BackCover);
log(info, "attached picture is an back album cover"); logger.info("attached picture is an back album cover");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE: case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::LeafletPage); frame->setType(TagLib::ID3v2::AttachedPictureFrame::LeafletPage);
log(info, "attached picture is a leflet from the album"); logger.info("attached picture is a leflet from the album");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA: case FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Media); frame->setType(TagLib::ID3v2::AttachedPictureFrame::Media);
log(info, "attached picture is probably an imadge of an album CD"); logger.info("attached picture is probably an imadge of an album CD");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST: case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::LeadArtist); frame->setType(TagLib::ID3v2::AttachedPictureFrame::LeadArtist);
log(info, "attached picture is an image of the lead artist"); logger.info("attached picture is an image of the lead artist");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST: case FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Artist); frame->setType(TagLib::ID3v2::AttachedPictureFrame::Artist);
log(info, "attached picture is an image the artist"); logger.info("attached picture is an image the artist");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR: case FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Conductor); frame->setType(TagLib::ID3v2::AttachedPictureFrame::Conductor);
log(info, "attached picture is an image the conductor"); logger.info("attached picture is an image the conductor");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND: case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Band); frame->setType(TagLib::ID3v2::AttachedPictureFrame::Band);
log(info, "attached picture is an image of the band"); logger.info("attached picture is an image of the band");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER: case FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Composer); frame->setType(TagLib::ID3v2::AttachedPictureFrame::Composer);
log(info, "attached picture is an image of composer"); logger.info("attached picture is an image of composer");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST: case FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Lyricist); frame->setType(TagLib::ID3v2::AttachedPictureFrame::Lyricist);
log(info, "attached picture is an image of the lyricist"); logger.info("attached picture is an image of the lyricist");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION: case FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::RecordingLocation); frame->setType(TagLib::ID3v2::AttachedPictureFrame::RecordingLocation);
log(info, "attached picture is an image recording location"); logger.info("attached picture is an image recording location");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING: case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::DuringRecording); frame->setType(TagLib::ID3v2::AttachedPictureFrame::DuringRecording);
log(info, "attached picture is an image of the process of the recording"); logger.info("attached picture is an image of the process of the recording");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE: case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::DuringPerformance); frame->setType(TagLib::ID3v2::AttachedPictureFrame::DuringPerformance);
log(info, "attached picture is an image of process of the performance"); logger.info("attached picture is an image of process of the performance");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE: case FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::MovieScreenCapture); frame->setType(TagLib::ID3v2::AttachedPictureFrame::MovieScreenCapture);
log(info, "attached picture is an frame from a movie"); logger.info("attached picture is an frame from a movie");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_FISH: case FLAC__STREAM_METADATA_PICTURE_TYPE_FISH:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::ColouredFish); frame->setType(TagLib::ID3v2::AttachedPictureFrame::ColouredFish);
log(info, "attached picture is ... a bright large colo(u?)red fish...? o_O"); logger.info("attached picture is ... a bright large colo(u?)red fish...? o_O");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION: case FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Illustration); frame->setType(TagLib::ID3v2::AttachedPictureFrame::Illustration);
log(info, "attached picture is an track related illustration"); logger.info("attached picture is an track related illustration");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE: case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::BandLogo); frame->setType(TagLib::ID3v2::AttachedPictureFrame::BandLogo);
log(info, "attached picture is a band logo"); logger.info("attached picture is a band logo");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE: case FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::PublisherLogo); frame->setType(TagLib::ID3v2::AttachedPictureFrame::PublisherLogo);
log(info, "attached picture is a publisher logo"); logger.info("attached picture is a publisher logo");
break; break;
case FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED: case FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED:
frame->setType(TagLib::ID3v2::AttachedPictureFrame::Other); frame->setType(TagLib::ID3v2::AttachedPictureFrame::Other);
log(info, "attached picture is something unknown, so, I would assume it's \"other\""); logger.info("attached picture is something unknown, so, I would assume it's \"other\"");
break; break;
} }
@ -487,11 +487,14 @@ void FLACtoMP3::attachPictureFrame(const FLAC__StreamMetadata_Picture& picture,
float KBytes = (float)sizeBytes / 1024; float KBytes = (float)sizeBytes / 1024;
std::string strKBytes = std::to_string(KBytes); std::string strKBytes = std::to_string(KBytes);
strKBytes = strKBytes.substr(0, strKBytes.find(".") + 3) + " KiB"; strKBytes = strKBytes.substr(0, strKBytes.find(".") + 3) + " KiB";
log(info, "attached picture size: " + strKBytes); logger.info("attached picture size: " + strKBytes);
std::string description = frame->description().to8Bit(); std::string description = frame->description().to8Bit();
if (description.size() > 0) if (description.size() > 0)
log(info, "attached picture has a description (b'cuz where else would you ever read it?): " + description); logger.info("attached picture has a description (b'cuz where else would you ever read it?): " + description);
id3v2tag.addFrame(frame); id3v2tag.addFrame(frame);
} }
std::list<Logger::Message> FLACtoMP3::getHistory() const {
return logger.getHistory();
}

View File

@ -10,17 +10,19 @@
#include <map> #include <map>
#include <stdio.h> #include <stdio.h>
#include "loggable.h" #include "logger/accumulator.h"
class FLACtoMP3 : public Loggable { class FLACtoMP3 {
public: public:
FLACtoMP3(Severity severity = info, uint8_t size = 4); FLACtoMP3(Logger::Severity severity = Logger::Severity::info, uint8_t size = 4);
~FLACtoMP3(); ~FLACtoMP3();
void setInputFile(const std::string& path); void setInputFile(const std::string& path);
void setOutputFile(const std::string& path); void setOutputFile(const std::string& path);
bool run(); bool run();
std::list<Logger::Message> getHistory() const;
private: private:
void processTags(const FLAC__StreamMetadata_VorbisComment& tags); void processTags(const FLAC__StreamMetadata_VorbisComment& tags);
void processInfo(const FLAC__StreamMetadata_StreamInfo& info); void processInfo(const FLAC__StreamMetadata_StreamInfo& info);
@ -41,6 +43,7 @@ private:
); );
private: private:
Accumulator logger;
std::string inPath; std::string inPath;
std::string outPath; std::string outPath;

View File

@ -1,36 +0,0 @@
#include "loggable.h"
constexpr std::array<std::string_view, Loggable::_sevetirySize> levels({
"debug",
"info",
"minor",
"major",
"warning",
"error",
"fatal"
});
Loggable::Loggable(Loggable::Severity severity):
currentSeverity(severity),
history()
{}
Loggable::~Loggable()
{}
void Loggable::log(Loggable::Severity severity, const std::string& comment) const {
if (severity >= currentSeverity)
history.emplace_back(severity, comment);
}
std::list<std::pair<Loggable::Severity, std::string>> Loggable::getHistory() const {
return history;
}
Loggable::Severity Loggable::stringToSeverity(const std::string& line) {
unsigned char dist = std::distance(levels.begin(), std::find(levels.begin(), levels.end(), line));
if (dist < _sevetirySize)
return static_cast<Severity>(dist);
return _sevetirySize;
}

View File

@ -1,34 +0,0 @@
#pragma once
#include <list>
#include <string>
#include <array>
#include <string_view>
#include <algorithm>
class Loggable {
public:
enum Severity {
debug,
info,
minor,
major,
warning,
error,
fatal,
_sevetirySize
};
typedef std::pair<Severity, std::string> Message;
Loggable(Severity severity);
~Loggable();
void log (Severity severity, const std::string& comment) const;
std::list<Message> getHistory() const;
static Severity stringToSeverity(const std::string& line);
private:
const Severity currentSeverity;
mutable std::list<Message> history;
};

13
src/logger/CMakeLists.txt Normal file
View File

@ -0,0 +1,13 @@
set(SOURCES
logger.cpp
printer.cpp
accumulator.cpp
)
set(HEADERS
logger.h
printer.h
accumulator.h
)
target_sources(${PROJECT_NAME} PRIVATE ${SOURCES})

View File

@ -0,0 +1,33 @@
#include "accumulator.h"
Accumulator::Accumulator(Severity severity):
severity(severity),
history()
{}
Logger::Severity Accumulator::getSeverity() const {
return severity;
}
void Accumulator::setSeverity(Severity severity) {
Accumulator::severity = severity;
}
void Accumulator::log(const std::list<Message>& comments, bool colored) const {
(void)(colored);
for (const Message& comment : comments)
if (comment.first >= Accumulator::severity)
history.emplace_back(comment);
}
void Accumulator::log(Severity severity, const std::string& comment, bool colored) const {
(void)(colored);
if (severity < Accumulator::severity)
return;
history.emplace_back(severity, comment);
}
std::list<Logger::Message> Accumulator::getHistory() const {
return history;
}

20
src/logger/accumulator.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include "logger.h"
class Accumulator : public Logger {
public:
Accumulator(Severity severity = Severity::info);
virtual void log(const std::list<Message>& comments, bool colored = true) const override;
virtual void log(Severity severity, const std::string& comment, bool colored = true) const override;
virtual Severity getSeverity() const override;
virtual void setSeverity(Severity severity) override;
std::list<Message> getHistory() const;
private:
Severity severity;
mutable std::list<Message> history;
};

53
src/logger/logger.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "logger.h"
#include <array>
#include <string_view>
#include <algorithm>
constexpr std::array<std::string_view, static_cast<int>(Logger::Severity::_sevetirySize)> levels({
"debug",
"info",
"minor",
"major",
"warning",
"error",
"fatal"
});
Logger::~Logger() {}
void Logger::debug(const std::string& comment, bool colored) const {
log(Severity::debug, comment, colored);
}
void Logger::info(const std::string& comment, bool colored) const {
log(Severity::info, comment, colored);
}
void Logger::minor(const std::string& comment, bool colored) const {
log(Severity::minor, comment, colored);
}
void Logger::major(const std::string& comment, bool colored) const {
log(Severity::major, comment, colored);
}
void Logger::warn(const std::string& comment, bool colored) const {
log(Severity::warning, comment, colored);
}
void Logger::error(const std::string& comment, bool colored) const {
log(Severity::error, comment, colored);
}
void Logger::fatal(const std::string& comment, bool colored) const {
log(Severity::fatal, comment, colored);
}
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<unsigned char>(Severity::_sevetirySize))
return static_cast<Severity>(dist);
return Severity::_sevetirySize;
}

36
src/logger/logger.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <list>
#include <string>
class Logger {
public:
enum class Severity {
debug,
info,
minor,
major,
warning,
error,
fatal,
_sevetirySize
};
using Message = std::pair<Severity, std::string>;
void debug(const std::string& comment, bool colored = true) const;
void info(const std::string& comment, bool colored = true) const;
void minor(const std::string& comment, bool colored = true) const;
void major(const std::string& comment, bool colored = true) const;
void warn(const std::string& comment, bool colored = true) const;
void error(const std::string& comment, bool colored = true) const;
void fatal(const std::string& comment, bool colored = true) const;
virtual ~Logger();
virtual void log(const std::list<Message>& comments, bool colored = true) const = 0;
virtual void log(Severity severity, const std::string& comment, bool colored = true) const = 0;
virtual void setSeverity(Severity severity) = 0;
virtual Severity getSeverity() const = 0;
static Severity stringToSeverity(const std::string& line);
};

135
src/logger/printer.cpp Normal file
View File

@ -0,0 +1,135 @@
#include "printer.h"
#include <array>
#include <string_view>
#include <iostream>
constexpr const std::array<std::string_view, static_cast<int>(Logger::Severity::_sevetirySize)> logSettings({
/*debug*/ "\e[90m",
/*info*/ "\e[32m",
/*minor*/ "\e[34m",
/*major*/ "\e[94m",
/*warning*/ "\e[33m",
/*error*/ "\e[31m",
/*fatal*/ "\e[91m"
});
constexpr const std::array<std::string_view, static_cast<int>(Logger::Severity::_sevetirySize)> logHeaders({
/*debug*/ "DEBUG: ",
/*info*/ "INFO: ",
/*minor*/ "MINOR: ",
/*major*/ "MAJOR: ",
/*warning*/ "WARNING: ",
/*error*/ "ERROR: ",
/*fatal*/ "FATAL: "
});
constexpr const std::string_view bold("\e[1m");
constexpr const std::string_view regular("\e[22m");
constexpr const std::string_view clearStyle("\e[0m");
constexpr const std::string_view clearLine("\e[2K\r");
Printer::Printer(Severity severity):
severity(severity),
mutex(),
status(std::nullopt)
{}
Logger::Severity Printer::getSeverity() const {
return severity;
}
void Printer::setSeverity(Severity severity) {
Printer::severity = severity;
}
void Printer::setStatusMessage(const std::string& message) {
if (status.has_value())
std::cout << clearLine;
status = message;
std::cout << message << std::flush;
}
void Printer::clearStatusMessage() {
if (status.has_value()) {
std::cout << clearLine << std::flush;
status = std::nullopt;
}
}
void Printer::printNested(
const std::string& header,
const std::vector<std::string>& lines,
const std::list<Message>& comments,
const std::optional<std::string>& status
) {
std::lock_guard lock(mutex);
if (comments.size() > 0) {
if (status.has_value())
std::cout << clearLine;
std::cout << bold << header << clearStyle << "\n";
for (const std::string& line : lines)
std::cout << line << "\n";
for (const Message& msg : comments) {
if (msg.first >= severity) {
std::cout << '\t';
printMessage(msg, true);
}
}
}
if (status.has_value())
Printer::status = status;
finishPrint(true);
}
void Printer::log(const std::list<Message>& comments, bool colored) const {
std::lock_guard lock(mutex);
bool changed = false;
for (const Message& msg : comments) {
if (msg.first >= severity) {
if (!changed && status.has_value())
std::cout << clearLine;
printMessage(msg, colored);
changed = true;
}
}
finishPrint(colored);
}
void Printer::log(Severity severity, const std::string& comment, bool colored) const {
if (severity < Printer::severity)
return;
std::lock_guard lock(mutex);
if (status.has_value())
std::cout << clearLine;
printMessage({severity, comment}, colored);
finishPrint(colored);
}
void Printer::finishPrint(bool colored) const {
if (colored)
std::cout << clearStyle;
if (status.has_value())
std::cout << '\r' << status.value();
std::cout << std::flush;
}
void Printer::printMessage(const Message& message, bool colored) const {
if (colored) {
int severity = static_cast<int>(message.first);
std::cout << logSettings[severity] << bold << logHeaders[severity] << regular;
}
std::cout << message.second << '\n';
}

38
src/logger/printer.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include <atomic>
#include <mutex>
#include <string>
#include <optional>
#include <vector>
#include "logger.h"
class Printer : public Logger {
public:
Printer(Severity severity = Severity::info);
virtual void log(const std::list<Message>& comments, bool colored = true) const override;
virtual void log(Severity severity, const std::string& comment, bool colored = true) const override;
virtual void setSeverity(Severity severity) override;
virtual Severity getSeverity() const override;
void setStatusMessage(const std::string& message);
void clearStatusMessage();
void printNested(
const std::string& header,
const std::vector<std::string>& lines = {},
const std::list<Message>& comments = {},
const std::optional<std::string>& status = std::nullopt
);
private:
void printMessage(const Message& message, bool colored = false) const;
void finishPrint(bool colored = false) const;
private:
std::atomic<Severity> severity;
mutable std::mutex mutex;
std::optional<std::string> status;
};

View File

@ -12,8 +12,10 @@
#include "collection.h" #include "collection.h"
#include "taskmanager.h" #include "taskmanager.h"
#include "settings.h" #include "settings.h"
#include "logger/logger.h"
int main(int argc, char **argv) { int main(int argc, char **argv) {
std::shared_ptr<Printer> logger = std::make_shared<Printer>();
std::shared_ptr<Settings> settings = std::make_shared<Settings>(argc, argv); std::shared_ptr<Settings> settings = std::make_shared<Settings>(argc, argv);
switch (settings->getAction()) { switch (settings->getAction()) {
@ -42,20 +44,33 @@ int main(int argc, char **argv) {
} }
} }
TaskManager taskManager(settings); std::string input = settings->getInput();
if (input.empty()) {
std::cout << "Input folder is not specified, quitting" << std::endl;
return -2;
}
std::string output = settings->getOutput();
if (output.empty()) {
std::cout << "Output folder is not specified, quitting" << std::endl;
return -3;
}
logger->setSeverity(settings->getLogLevel());
TaskManager taskManager(settings, logger);
taskManager.start(); taskManager.start();
std::chrono::time_point start = std::chrono::system_clock::now(); std::chrono::time_point start = std::chrono::system_clock::now();
Collection collection(settings->getInput(), &taskManager); Collection collection(input, &taskManager);
collection.convert(settings->getOutput()); collection.convert(output);
taskManager.printProgress();
taskManager.wait(); taskManager.wait();
std::cout << std::endl;
taskManager.stop();
std::chrono::time_point end = std::chrono::system_clock::now(); std::chrono::time_point end = std::chrono::system_clock::now();
std::chrono::duration<double> seconds = end - start; std::chrono::duration<double> seconds = end - start;
std::cout << std::endl << "Encoding is done, it took " << seconds.count() << " seconds in total, enjoy!" << std::endl; std::cout << "Encoding is done, it took " << seconds.count() << " seconds in total, enjoy!" << std::endl;
taskManager.stop();
return 0; return 0;
} }

View File

@ -55,7 +55,7 @@ void Settings::parseArguments() {
} }
if (!output.has_value()) { if (!output.has_value()) {
input = arg; output = arg;
continue; continue;
} }
} }
@ -101,11 +101,11 @@ bool Settings::isConfigDefault() const {
return !configPath.has_value(); return !configPath.has_value();
} }
Loggable::Severity Settings::getLogLevel() const { Logger::Severity Settings::getLogLevel() const {
if (logLevel.has_value()) if (logLevel.has_value())
return logLevel.value(); return logLevel.value();
else else
return Loggable::info; return Logger::Severity::info;
} }
std::string_view Settings::stripFlags(const std::string_view& option) { std::string_view Settings::stripFlags(const std::string_view& option) {
@ -166,8 +166,8 @@ void Settings::readConfigLine(const std::string& line) {
case level: { case level: {
std::string lv; std::string lv;
if (stream >> lv) { if (stream >> lv) {
Loggable::Severity level = Loggable::stringToSeverity(lv); Logger::Severity level = Logger::stringToSeverity(lv);
if (level < Loggable::_sevetirySize) if (level < Logger::Severity::_sevetirySize)
logLevel = level; logLevel = level;
} }
} break; } break;

View File

@ -12,7 +12,7 @@
#include <cctype> #include <cctype>
#include <sstream> #include <sstream>
#include "loggable.h" #include "logger/logger.h"
class Settings { class Settings {
public: public:
@ -40,7 +40,7 @@ public:
std::string getOutput() const; std::string getOutput() const;
std::string getConfigPath() const; std::string getConfigPath() const;
bool isConfigDefault() const; bool isConfigDefault() const;
Loggable::Severity getLogLevel() const; Logger::Severity getLogLevel() const;
Type getType() const; Type getType() const;
Action getAction() const; Action getAction() const;
@ -66,6 +66,6 @@ private:
std::optional<Type> outputType; std::optional<Type> outputType;
std::optional<std::string> input; std::optional<std::string> input;
std::optional<std::string> output; std::optional<std::string> output;
std::optional<Loggable::Severity> logLevel; std::optional<Logger::Severity> logLevel;
std::optional<std::string> configPath; std::optional<std::string> configPath;
}; };

View File

@ -2,41 +2,19 @@
#include "flactomp3.h" #include "flactomp3.h"
constexpr const std::array<std::string_view, Loggable::fatal + 1> logSettings({ TaskManager::TaskManager(const std::shared_ptr<Settings>& settings, const std::shared_ptr<Printer>& logger):
/*debug*/ "\e[90m",
/*info*/ "\e[32m",
/*minor*/ "\e[34m",
/*major*/ "\e[94m",
/*warning*/ "\e[33m",
/*error*/ "\e[31m",
/*fatal*/ "\e[91m"
});
constexpr const std::array<std::string_view, Loggable::fatal + 1> logHeaders({
/*debug*/ "DEBUG: ",
/*info*/ "INFO: ",
/*minor*/ "MINOR: ",
/*major*/ "MAJOR: ",
/*warning*/ "WARNING: ",
/*error*/ "ERROR: ",
/*fatal*/ "FATAL: "
});
TaskManager::TaskManager(const std::shared_ptr<Settings>& settings):
settings(settings), settings(settings),
logger(logger),
busyThreads(0), busyThreads(0),
maxTasks(0), maxTasks(0),
completeTasks(0), completeTasks(0),
terminate(false), terminate(false),
printMutex(), running(false),
queueMutex(), queueMutex(),
busyMutex(),
loopConditional(), loopConditional(),
waitConditional(), waitConditional(),
threads(), threads(),
jobs(), jobs()
boundLoopCondition(std::bind(&TaskManager::loopCondition, this)),
boundWaitCondition(std::bind(&TaskManager::waitCondition, this))
{ {
} }
@ -44,126 +22,107 @@ TaskManager::~TaskManager() {
} }
void TaskManager::queueJob(const std::filesystem::path& source, const std::filesystem::path& destination) { void TaskManager::queueJob(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(source, destination);
}
++maxTasks; ++maxTasks;
logger->setStatusMessage(std::to_string(completeTasks) + "/" + std::to_string(maxTasks));
lock.unlock();
loopConditional.notify_one(); loopConditional.notify_one();
waitConditional.notify_all();
} }
bool TaskManager::busy() const { bool TaskManager::busy() const {
bool result; std::lock_guard lock(queueMutex);
{ return !jobs.empty();
std::unique_lock<std::mutex> lock(queueMutex);
result = !jobs.empty();
}
return result;
} }
void TaskManager::start() { void TaskManager::start() {
std::lock_guard lock(queueMutex);
if (running)
return;
const uint32_t num_threads = std::thread::hardware_concurrency(); const uint32_t num_threads = std::thread::hardware_concurrency();
for (uint32_t ii = 0; ii < num_threads; ++ii) for (uint32_t ii = 0; ii < num_threads; ++ii)
threads.emplace_back(std::thread(&TaskManager::loop, this)); threads.emplace_back(std::thread(&TaskManager::loop, this));
running = true;
} }
void TaskManager::loop() { void TaskManager::loop() {
while (true) { while (true) {
std::pair<std::string, std::string> pair; std::unique_lock<std::mutex> lock(queueMutex);
{ while (!terminate && jobs.empty())
std::unique_lock<std::mutex> lock(queueMutex); loopConditional.wait(lock);
loopConditional.wait(lock, boundLoopCondition);
if (terminate)
return;
pair = jobs.front(); if (terminate)
++busyThreads; return;
waitConditional.notify_all();
jobs.pop(); std::pair<std::string, std::string> pair = jobs.front();
} ++busyThreads;
jobs.pop();
lock.unlock();
JobResult result; JobResult result;
switch (settings->getType()) { switch (settings->getType()) {
case Settings::mp3: case Settings::mp3:
result = mp3Job(pair.first, pair.second + ".mp3"); result = mp3Job(pair.first, pair.second + ".mp3", settings->getLogLevel());
break;
default:
break; break;
} }
lock.lock();
++completeTasks; ++completeTasks;
printProgress(result, pair.first, pair.second); 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)
);
--busyThreads; --busyThreads;
lock.unlock();
waitConditional.notify_all(); waitConditional.notify_all();
} }
} }
bool TaskManager::loopCondition() const {
return !jobs.empty() || terminate;
}
bool TaskManager::waitCondition() const {
return busyThreads == 0 && !busy();
}
void TaskManager::stop() { void TaskManager::stop() {
{ std::unique_lock lock(queueMutex);
std::unique_lock<std::mutex> lock(queueMutex); if (!running)
terminate = true; return;
}
terminate = true;
lock.unlock();
loopConditional.notify_all(); loopConditional.notify_all();
for (std::thread& thread : threads) for (std::thread& thread : threads)
thread.join(); thread.join();
lock.lock();
threads.clear(); threads.clear();
running = false;
logger->clearStatusMessage();
} }
void TaskManager::wait() { void TaskManager::wait() {
std::unique_lock<std::mutex> lock(busyMutex); std::unique_lock lock(queueMutex);
waitConditional.wait(lock, boundWaitCondition); while (busyThreads != 0 || !jobs.empty())
waitConditional.wait(lock);
} }
TaskManager::JobResult TaskManager::mp3Job(const std::filesystem::path& source, const std::filesystem::path& destination) { unsigned int TaskManager::getCompleteTasks() const {
FLACtoMP3 convertor(Loggable::debug); std::lock_guard lock(queueMutex);
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.setInputFile(source);
convertor.setOutputFile(destination); convertor.setOutputFile(destination);
bool result = convertor.run(); bool result = convertor.run();
return {result, convertor.getHistory()}; return {result, convertor.getHistory()};
} }
void TaskManager::printProgress() const {
std::unique_lock<std::mutex> lock(printMutex);
std::cout << "\r" << completeTasks << "/" << maxTasks << std::flush;
}
void TaskManager::printProgress(
const TaskManager::JobResult& result,
const std::filesystem::path& source,
const std::filesystem::path& destination) const
{
std::unique_lock<std::mutex> lock(printMutex);
if (result.first) {
if (result.second.size() > 0) {
std::cout << "\r\e[1m" << "Encoding complete but there are messages about it" << "\e[0m" << std::endl;
printLog(result, source, destination);
}
} else {
std::cout << "\r\e[1m" << "Encoding failed!" << "\e[0m" << std::endl;
printLog(result, source, destination);
}
std::cout << "\r" << completeTasks << "/" << maxTasks << std::flush;
}
void TaskManager::printLog(
const TaskManager::JobResult& result,
const std::filesystem::path& source,
const std::filesystem::path& destination)
{
std::cout << "Source: \t" << source << std::endl;
std::cout << "Destination: \t" << destination << std::endl;
for (const Loggable::Message& msg : result.second) {
std::cout << "\t" << logSettings[msg.first] << "\e[1m" << logHeaders[msg.first] << "\e[22m" << msg.second << std::endl;
}
std::cout << "\e[0m" << std::endl;
}

View File

@ -14,13 +14,13 @@
#include <array> #include <array>
#include <memory> #include <memory>
#include "loggable.h"
#include "settings.h" #include "settings.h"
#include "logger/printer.h"
class TaskManager { class TaskManager {
typedef std::pair<bool, std::list<Loggable::Message>> JobResult; typedef std::pair<bool, std::list<Logger::Message>> JobResult;
public: public:
TaskManager(const std::shared_ptr<Settings>& settings); TaskManager(const std::shared_ptr<Settings>& settings, const std::shared_ptr<Printer>& logger);
~TaskManager(); ~TaskManager();
void start(); void start();
@ -28,30 +28,25 @@ public:
void stop(); void stop();
bool busy() const; bool busy() const;
void wait(); void wait();
void printProgress() const;
void printProgress(const JobResult& result, const std::filesystem::path& source, const std::filesystem::path& destination) const; unsigned int getCompleteTasks() const;
private: private:
void loop(); void loop();
bool loopCondition() const; static JobResult mp3Job(const std::filesystem::path& source, const std::filesystem::path& destination, Logger::Severity logLevel);
bool waitCondition() const;
static JobResult mp3Job(const std::filesystem::path& source, const std::filesystem::path& destination);
static void printLog(const JobResult& result, const std::filesystem::path& source, const std::filesystem::path& destination);
private: private:
std::shared_ptr<Settings> settings; std::shared_ptr<Settings> settings;
std::atomic<uint32_t> busyThreads; std::shared_ptr<Printer> logger;
std::atomic<uint32_t> maxTasks; unsigned int busyThreads;
std::atomic<uint32_t> completeTasks; unsigned int maxTasks;
unsigned int completeTasks;
bool terminate; bool terminate;
mutable std::mutex printMutex; bool running;
mutable std::mutex queueMutex; mutable std::mutex queueMutex;
std::mutex busyMutex;
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<std::pair<std::filesystem::path, std::filesystem::path>> jobs;
std::function<bool()> boundLoopCondition;
std::function<bool()> boundWaitCondition;
}; };