From 0b268a7245c5c080b4779967cff02dea690c33d9 Mon Sep 17 00:00:00 2001 From: blue Date: Sun, 8 Oct 2023 20:29:40 -0300 Subject: [PATCH] changed logging concept, reworked task manager --- CHANGELOG.md | 4 + CMakeLists.txt | 2 +- src/CMakeLists.txt | 4 +- src/flactomp3.cpp | 103 ++++++++++++----------- src/flactomp3.h | 9 +- src/loggable.cpp | 36 -------- src/loggable.h | 34 -------- src/logger/CMakeLists.txt | 13 +++ src/logger/accumulator.cpp | 33 ++++++++ src/logger/accumulator.h | 20 +++++ src/logger/logger.cpp | 53 ++++++++++++ src/logger/logger.h | 36 ++++++++ src/logger/printer.cpp | 135 ++++++++++++++++++++++++++++++ src/logger/printer.h | 38 +++++++++ src/main.cpp | 29 +++++-- src/settings.cpp | 10 +-- src/settings.h | 6 +- src/taskmanager.cpp | 165 ++++++++++++++----------------------- src/taskmanager.h | 27 +++--- 19 files changed, 497 insertions(+), 260 deletions(-) delete mode 100644 src/loggable.cpp delete mode 100644 src/loggable.h create mode 100644 src/logger/CMakeLists.txt create mode 100644 src/logger/accumulator.cpp create mode 100644 src/logger/accumulator.h create mode 100644 src/logger/logger.cpp create mode 100644 src/logger/logger.h create mode 100644 src/logger/printer.cpp create mode 100644 src/logger/printer.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 159603d..8fa2da4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # 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) - Better way of handling tags using TagLib diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bee5d8..ea5a1f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.5) project( mlc VERSION 1.2.0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b526755..26c19d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,6 @@ set(SOURCES flactomp3.cpp collection.cpp taskmanager.cpp - loggable.cpp settings.cpp ) @@ -22,11 +21,12 @@ set(HEADERS flactomp3.h collection.h taskmanager.h - loggable.h settings.h ) target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) +add_subdirectory(logger) + make_includable(default.conf ${CMAKE_BINARY_DIR}/generated/default.conf) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR}) diff --git a/src/flactomp3.cpp b/src/flactomp3.cpp index d03523f..5b01c1c 100644 --- a/src/flactomp3.cpp +++ b/src/flactomp3.cpp @@ -13,8 +13,8 @@ const std::map textIdentificationReplacements({ {"PUBLISHER", "TPUB"} }); -FLACtoMP3::FLACtoMP3(Severity severity, uint8_t size) : - Loggable(severity), +FLACtoMP3::FLACtoMP3(Logger::Severity severity, uint8_t size) : + logger(severity), inPath(), outPath(), decoder(FLAC__stream_decoder_new()), @@ -72,7 +72,7 @@ bool FLACtoMP3::run() { float MBytes = (float)fileSize / 1024 / 1024; std::string strMBytes = std::to_string(MBytes); strMBytes = strMBytes.substr(0, strMBytes.find(".") + 3) + " MiB"; - log(info, "resulting file size: " + strMBytes); + logger.info("resulting file size: " + strMBytes); } return ok; @@ -110,13 +110,13 @@ bool FLACtoMP3::initializeOutput() { output = fopen(outPath.c_str(), "w+b"); if (output == 0) { output = nullptr; - log(fatal, "Error opening file " + outPath); + logger.fatal("Error opening file " + outPath); return false; } int ret = lame_init_params(encoder); 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); output = nullptr; return false; @@ -143,9 +143,9 @@ void FLACtoMP3::processInfo(const FLAC__StreamMetadata_StreamInfo& info) { lame_set_in_samplerate(encoder, info.sample_rate); lame_set_num_channels(encoder, info.channels); flacMaxBlockSize = info.max_blocksize; - log(Loggable::info, "sample rate: " + std::to_string(info.sample_rate)); - log(Loggable::info, "channels: " + std::to_string(info.channels)); - log(Loggable::info, "bits per sample: " + std::to_string(info.bits_per_sample)); + logger.info("sample rate: " + std::to_string(info.sample_rate)); + logger.info("channels: " + std::to_string(info.channels)); + logger.info("bits per sample: " + std::to_string(info.bits_per_sample)); } 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::size_type ePos = comm.find("="); 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; } 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()); frame->setText(value); customFrames.push_back(frame); - log(debug, "tag \"" + key + "\" was remapped to \"" + itr->second + "\""); + logger.debug("tag \"" + key + "\" was remapped to \"" + itr->second + "\""); } else { success = props.insert(key, TagLib::String(value, TagLib::String::UTF8)); } if (!success) - log(warning, "couldn't understand tag (" + key + "), skipping"); + logger.warn("couldn't understand tag (" + key + "), skipping"); } TagLib::StringList unsupported = props.unsupportedData(); 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); @@ -195,13 +195,13 @@ void FLACtoMP3::processTags(const FLAC__StreamMetadata_VorbisComment& tags) { void FLACtoMP3::processPicture(const FLAC__StreamMetadata_Picture& picture) { if (downscaleAlbumArt && picture.data_length > LAME_MAXALBUMART) { - log(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.info("embeded album art is too big (" + std::to_string(picture.data_length) + " bytes), rescaling"); + logger.debug("mime type is " + std::string(picture.mime_type)); if (picture.mime_type == jpeg) { if (scaleJPEG(picture)) - log(debug, "successfully rescaled album art"); + logger.debug("successfully rescaled album art"); else - log(warning, "failed to rescale album art"); + logger.warn("failed to rescale album art"); } } else { //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); if (rc != 1) { - log(Loggable::error, "error reading jpeg header"); + logger.error("error reading jpeg header"); return false; } 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) { if (mem_size + rowSize > vector.size()) { 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_write_scanlines(&cinfo, &row, 1); @@ -297,11 +297,11 @@ bool FLACtoMP3::flush() { outputBufferSize ); 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; delete[] outputBuffer; 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( encoder, @@ -318,10 +318,10 @@ bool FLACtoMP3::flush() { return actuallyWritten == 1; } else { if (nwrite == 0) { - log(minor, "encoding flush encoded 0 bytes, skipping write"); + logger.minor("encoding flush encoded 0 bytes, skipping write"); return true; } else { - log(fatal, "encoding flush failed. Code = : " + std::to_string(nwrite)); + logger.fatal("encoding flush failed. Code = : " + std::to_string(nwrite)); return false; } } @@ -359,15 +359,15 @@ FLAC__StreamDecoderWriteStatus FLACtoMP3::write( // } FLACtoMP3* self = static_cast(client_data); 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; } 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; } 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; } @@ -383,7 +383,7 @@ void FLACtoMP3::error(const FLAC__StreamDecoder* decoder, FLAC__StreamDecoderErr (void)decoder; FLACtoMP3* self = static_cast(client_data); 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) { @@ -395,91 +395,91 @@ void FLACtoMP3::attachPictureFrame(const FLAC__StreamMetadata_Picture& picture, switch (picture.type) { case FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER: frame->setType(TagLib::ID3v2::AttachedPictureFrame::Other); - log(info, "attached picture is described as \"other\""); + logger.info("attached picture is described as \"other\""); break; case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_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; case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_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; case FLAC__STREAM_METADATA_PICTURE_TYPE_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; case FLAC__STREAM_METADATA_PICTURE_TYPE_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; case FLAC__STREAM_METADATA_PICTURE_TYPE_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; case FLAC__STREAM_METADATA_PICTURE_TYPE_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; case FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_FISH: 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; case FLAC__STREAM_METADATA_PICTURE_TYPE_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; case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE: frame->setType(TagLib::ID3v2::AttachedPictureFrame::BandLogo); - log(info, "attached picture is a band logo"); + logger.info("attached picture is a band logo"); break; case FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE: frame->setType(TagLib::ID3v2::AttachedPictureFrame::PublisherLogo); - log(info, "attached picture is a publisher logo"); + logger.info("attached picture is a publisher logo"); break; case FLAC__STREAM_METADATA_PICTURE_TYPE_UNDEFINED: 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; } @@ -487,11 +487,14 @@ void FLACtoMP3::attachPictureFrame(const FLAC__StreamMetadata_Picture& picture, float KBytes = (float)sizeBytes / 1024; std::string strKBytes = std::to_string(KBytes); 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(); 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); } +std::list FLACtoMP3::getHistory() const { + return logger.getHistory(); +} diff --git a/src/flactomp3.h b/src/flactomp3.h index 8d098fa..6ebefc6 100644 --- a/src/flactomp3.h +++ b/src/flactomp3.h @@ -10,17 +10,19 @@ #include #include -#include "loggable.h" +#include "logger/accumulator.h" -class FLACtoMP3 : public Loggable { +class FLACtoMP3 { public: - FLACtoMP3(Severity severity = info, uint8_t size = 4); + FLACtoMP3(Logger::Severity severity = Logger::Severity::info, uint8_t size = 4); ~FLACtoMP3(); void setInputFile(const std::string& path); void setOutputFile(const std::string& path); bool run(); + std::list getHistory() const; + private: void processTags(const FLAC__StreamMetadata_VorbisComment& tags); void processInfo(const FLAC__StreamMetadata_StreamInfo& info); @@ -41,6 +43,7 @@ private: ); private: + Accumulator logger; std::string inPath; std::string outPath; diff --git a/src/loggable.cpp b/src/loggable.cpp deleted file mode 100644 index c0bd2e0..0000000 --- a/src/loggable.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "loggable.h" - -constexpr std::array 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> 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(dist); - - return _sevetirySize; -} diff --git a/src/loggable.h b/src/loggable.h deleted file mode 100644 index 999defe..0000000 --- a/src/loggable.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -class Loggable { -public: - enum Severity { - debug, - info, - minor, - major, - warning, - error, - fatal, - _sevetirySize - }; - typedef std::pair Message; - - Loggable(Severity severity); - ~Loggable(); - - void log (Severity severity, const std::string& comment) const; - std::list getHistory() const; - - static Severity stringToSeverity(const std::string& line); - -private: - const Severity currentSeverity; - mutable std::list history; -}; diff --git a/src/logger/CMakeLists.txt b/src/logger/CMakeLists.txt new file mode 100644 index 0000000..6188886 --- /dev/null +++ b/src/logger/CMakeLists.txt @@ -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}) diff --git a/src/logger/accumulator.cpp b/src/logger/accumulator.cpp new file mode 100644 index 0000000..70eca0c --- /dev/null +++ b/src/logger/accumulator.cpp @@ -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& 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 Accumulator::getHistory() const { + return history; +} diff --git a/src/logger/accumulator.h b/src/logger/accumulator.h new file mode 100644 index 0000000..673a6e7 --- /dev/null +++ b/src/logger/accumulator.h @@ -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& 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 getHistory() const; + +private: + Severity severity; + mutable std::list history; +}; diff --git a/src/logger/logger.cpp b/src/logger/logger.cpp new file mode 100644 index 0000000..9be0ef6 --- /dev/null +++ b/src/logger/logger.cpp @@ -0,0 +1,53 @@ +#include "logger.h" + +#include +#include +#include + +constexpr std::array(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(Severity::_sevetirySize)) + return static_cast(dist); + + return Severity::_sevetirySize; +} diff --git a/src/logger/logger.h b/src/logger/logger.h new file mode 100644 index 0000000..65b00c7 --- /dev/null +++ b/src/logger/logger.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +class Logger { +public: + enum class Severity { + debug, + info, + minor, + major, + warning, + error, + fatal, + _sevetirySize + }; + using Message = std::pair; + + 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& 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); +}; diff --git a/src/logger/printer.cpp b/src/logger/printer.cpp new file mode 100644 index 0000000..8c1f49c --- /dev/null +++ b/src/logger/printer.cpp @@ -0,0 +1,135 @@ +#include "printer.h" + +#include +#include +#include + +constexpr const std::array(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(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& lines, + const std::list& comments, + const std::optional& 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& 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(message.first); + std::cout << logSettings[severity] << bold << logHeaders[severity] << regular; + } + std::cout << message.second << '\n'; +} diff --git a/src/logger/printer.h b/src/logger/printer.h new file mode 100644 index 0000000..3789811 --- /dev/null +++ b/src/logger/printer.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "logger.h" + +class Printer : public Logger { +public: + Printer(Severity severity = Severity::info); + + virtual void log(const std::list& 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& lines = {}, + const std::list& comments = {}, + const std::optional& status = std::nullopt + ); + +private: + void printMessage(const Message& message, bool colored = false) const; + void finishPrint(bool colored = false) const; + +private: + std::atomic severity; + mutable std::mutex mutex; + std::optional status; +}; diff --git a/src/main.cpp b/src/main.cpp index 35df94e..dedcae5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,8 +12,10 @@ #include "collection.h" #include "taskmanager.h" #include "settings.h" +#include "logger/logger.h" int main(int argc, char **argv) { + std::shared_ptr logger = std::make_shared(); std::shared_ptr settings = std::make_shared(argc, argv); 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(); std::chrono::time_point start = std::chrono::system_clock::now(); - Collection collection(settings->getInput(), &taskManager); - collection.convert(settings->getOutput()); + Collection collection(input, &taskManager); + collection.convert(output); - taskManager.printProgress(); taskManager.wait(); + std::cout << std::endl; + taskManager.stop(); + std::chrono::time_point end = std::chrono::system_clock::now(); std::chrono::duration seconds = end - start; - std::cout << std::endl << "Encoding is done, it took " << seconds.count() << " seconds in total, enjoy!" << std::endl; - - taskManager.stop(); + std::cout << "Encoding is done, it took " << seconds.count() << " seconds in total, enjoy!" << std::endl; return 0; } diff --git a/src/settings.cpp b/src/settings.cpp index d519f37..01dc1a3 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -55,7 +55,7 @@ void Settings::parseArguments() { } if (!output.has_value()) { - input = arg; + output = arg; continue; } } @@ -101,11 +101,11 @@ bool Settings::isConfigDefault() const { return !configPath.has_value(); } -Loggable::Severity Settings::getLogLevel() const { +Logger::Severity Settings::getLogLevel() const { if (logLevel.has_value()) return logLevel.value(); else - return Loggable::info; + return Logger::Severity::info; } std::string_view Settings::stripFlags(const std::string_view& option) { @@ -166,8 +166,8 @@ void Settings::readConfigLine(const std::string& line) { case level: { std::string lv; if (stream >> lv) { - Loggable::Severity level = Loggable::stringToSeverity(lv); - if (level < Loggable::_sevetirySize) + Logger::Severity level = Logger::stringToSeverity(lv); + if (level < Logger::Severity::_sevetirySize) logLevel = level; } } break; diff --git a/src/settings.h b/src/settings.h index fb45bef..c8b4293 100644 --- a/src/settings.h +++ b/src/settings.h @@ -12,7 +12,7 @@ #include #include -#include "loggable.h" +#include "logger/logger.h" class Settings { public: @@ -40,7 +40,7 @@ public: std::string getOutput() const; std::string getConfigPath() const; bool isConfigDefault() const; - Loggable::Severity getLogLevel() const; + Logger::Severity getLogLevel() const; Type getType() const; Action getAction() const; @@ -66,6 +66,6 @@ private: std::optional outputType; std::optional input; std::optional output; - std::optional logLevel; + std::optional logLevel; std::optional configPath; }; diff --git a/src/taskmanager.cpp b/src/taskmanager.cpp index 57617aa..d1a9bad 100644 --- a/src/taskmanager.cpp +++ b/src/taskmanager.cpp @@ -2,41 +2,19 @@ #include "flactomp3.h" -constexpr const std::array 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 logHeaders({ - /*debug*/ "DEBUG: ", - /*info*/ "INFO: ", - /*minor*/ "MINOR: ", - /*major*/ "MAJOR: ", - /*warning*/ "WARNING: ", - /*error*/ "ERROR: ", - /*fatal*/ "FATAL: " -}); - -TaskManager::TaskManager(const std::shared_ptr& settings): +TaskManager::TaskManager(const std::shared_ptr& settings, const std::shared_ptr& logger): settings(settings), + logger(logger), busyThreads(0), maxTasks(0), completeTasks(0), terminate(false), - printMutex(), + running(false), queueMutex(), - busyMutex(), loopConditional(), waitConditional(), threads(), - jobs(), - boundLoopCondition(std::bind(&TaskManager::loopCondition, this)), - boundWaitCondition(std::bind(&TaskManager::waitCondition, this)) + jobs() { } @@ -44,126 +22,107 @@ TaskManager::~TaskManager() { } void TaskManager::queueJob(const std::filesystem::path& source, const std::filesystem::path& destination) { - { - std::unique_lock lock(queueMutex); - jobs.emplace(source, destination); - } + std::unique_lock lock(queueMutex); + jobs.emplace(source, destination); + ++maxTasks; + logger->setStatusMessage(std::to_string(completeTasks) + "/" + std::to_string(maxTasks)); + + lock.unlock(); loopConditional.notify_one(); - waitConditional.notify_all(); } bool TaskManager::busy() const { - bool result; - { - std::unique_lock lock(queueMutex); - result = !jobs.empty(); - } - - return result; + std::lock_guard lock(queueMutex); + return !jobs.empty(); } void TaskManager::start() { + std::lock_guard lock(queueMutex); + if (running) + return; + const uint32_t num_threads = std::thread::hardware_concurrency(); for (uint32_t ii = 0; ii < num_threads; ++ii) threads.emplace_back(std::thread(&TaskManager::loop, this)); + + running = true; } void TaskManager::loop() { while (true) { - std::pair pair; - { - std::unique_lock lock(queueMutex); - loopConditional.wait(lock, boundLoopCondition); - if (terminate) - return; + std::unique_lock lock(queueMutex); + while (!terminate && jobs.empty()) + loopConditional.wait(lock); - pair = jobs.front(); - ++busyThreads; - waitConditional.notify_all(); - jobs.pop(); - } + if (terminate) + return; + + std::pair pair = jobs.front(); + ++busyThreads; + jobs.pop(); + lock.unlock(); JobResult result; switch (settings->getType()) { case Settings::mp3: - result = mp3Job(pair.first, pair.second + ".mp3"); + result = mp3Job(pair.first, pair.second + ".mp3", settings->getLogLevel()); + break; + default: break; } + lock.lock(); ++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; + lock.unlock(); waitConditional.notify_all(); } } -bool TaskManager::loopCondition() const { - return !jobs.empty() || terminate; -} - -bool TaskManager::waitCondition() const { - return busyThreads == 0 && !busy(); -} - void TaskManager::stop() { - { - std::unique_lock lock(queueMutex); - terminate = true; - } + std::unique_lock lock(queueMutex); + if (!running) + return; + terminate = true; + + lock.unlock(); loopConditional.notify_all(); for (std::thread& thread : threads) thread.join(); + lock.lock(); threads.clear(); + running = false; + logger->clearStatusMessage(); } void TaskManager::wait() { - std::unique_lock lock(busyMutex); - waitConditional.wait(lock, boundWaitCondition); + std::unique_lock lock(queueMutex); + while (busyThreads != 0 || !jobs.empty()) + waitConditional.wait(lock); } -TaskManager::JobResult TaskManager::mp3Job(const std::filesystem::path& source, const std::filesystem::path& destination) { - FLACtoMP3 convertor(Loggable::debug); +unsigned int TaskManager::getCompleteTasks() const { + 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.setOutputFile(destination); bool result = convertor.run(); return {result, convertor.getHistory()}; } - -void TaskManager::printProgress() const { - std::unique_lock 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 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; -} diff --git a/src/taskmanager.h b/src/taskmanager.h index d0f935f..7100135 100644 --- a/src/taskmanager.h +++ b/src/taskmanager.h @@ -14,13 +14,13 @@ #include #include -#include "loggable.h" #include "settings.h" +#include "logger/printer.h" class TaskManager { - typedef std::pair> JobResult; + typedef std::pair> JobResult; public: - TaskManager(const std::shared_ptr& settings); + TaskManager(const std::shared_ptr& settings, const std::shared_ptr& logger); ~TaskManager(); void start(); @@ -28,30 +28,25 @@ public: void stop(); bool busy() const; 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: void loop(); - bool loopCondition() const; - 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); + static JobResult mp3Job(const std::filesystem::path& source, const std::filesystem::path& destination, Logger::Severity logLevel); private: std::shared_ptr settings; - std::atomic busyThreads; - std::atomic maxTasks; - std::atomic completeTasks; + std::shared_ptr logger; + unsigned int busyThreads; + unsigned int maxTasks; + unsigned int completeTasks; bool terminate; - mutable std::mutex printMutex; + bool running; mutable std::mutex queueMutex; - std::mutex busyMutex; std::condition_variable loopConditional; std::condition_variable waitConditional; std::vector threads; std::queue> jobs; - std::function boundLoopCondition; - std::function boundWaitCondition; };