Adapt to new notification handling

This commit is contained in:
Martchus 2018-03-06 02:04:35 +01:00
parent 254b9b7661
commit c2b2e4ac44
18 changed files with 368 additions and 345 deletions

View File

@ -5,6 +5,7 @@
#include <c++utilities/conversion/conversionexception.h>
#include <c++utilities/conversion/stringconversion.h>
#include <c++utilities/conversion/stringbuilder.h>
#include <cstring>
#include <iostream>
@ -37,7 +38,7 @@ void AttachmentInfo::parseDenotation(const char *denotation)
}
}
void AttachmentInfo::apply(AbstractContainer *container)
void AttachmentInfo::apply(AbstractContainer *container, Media::Diagnostics &diag)
{
static const string context("applying specified attachments");
AbstractAttachment *attachment = nullptr;
@ -48,30 +49,30 @@ void AttachmentInfo::apply(AbstractContainer *container)
cerr << "Argument --update-argument specified but no name/path provided." << endl;
return;
}
apply(container->createAttachment());
apply(container->createAttachment(), diag);
break;
case AttachmentAction::Update:
if(hasId) {
for(size_t i = 0, count = container->attachmentCount(); i < count; ++i) {
attachment = container->attachment(i);
if(attachment->id() == id) {
apply(attachment);
apply(attachment, diag);
attachmentFound = true;
}
}
if(!attachmentFound) {
container->addNotification(NotificationType::Critical, "Attachment with the specified ID \"" + numberToString(id) + "\" does not exist and hence can't be updated.", context);
diag.emplace_back(DiagLevel::Critical, argsToString("Attachment with the specified ID \"", id, "\" does not exist and hence can't be updated."), context);
}
} else if(name) {
for(size_t i = 0, count = container->attachmentCount(); i < count; ++i) {
attachment = container->attachment(i);
if(attachment->name() == name) {
apply(attachment);
apply(attachment, diag);
attachmentFound = true;
}
}
if(!attachmentFound) {
container->addNotification(NotificationType::Critical, "Attachment with the specified name \"" + string(name) + "\" does not exist and hence can't be updated.", context);
diag.emplace_back(DiagLevel::Critical, argsToString("Attachment with the specified name \"", name, "\" does not exist and hence can't be updated."), context);
}
} else {
cerr << "Argument --update-argument specified but no ID/name provided." << endl;
@ -87,7 +88,7 @@ void AttachmentInfo::apply(AbstractContainer *container)
}
}
if(!attachmentFound) {
container->addNotification(NotificationType::Critical, "Attachment with the specified ID \"" + numberToString(id) + "\" does not exist and hence can't be removed.", context);
diag.emplace_back(DiagLevel::Critical, "Attachment with the specified ID \"" + numberToString(id) + "\" does not exist and hence can't be removed.", context);
}
} else if(name) {
for(size_t i = 0, count = container->attachmentCount(); i < count; ++i) {
@ -98,7 +99,7 @@ void AttachmentInfo::apply(AbstractContainer *container)
}
}
if(!attachmentFound) {
container->addNotification(NotificationType::Critical, "Attachment with the specified name \"" + string(name) + "\" does not exist and hence can't be removed.", context);
diag.emplace_back(DiagLevel::Critical, "Attachment with the specified name \"" + string(name) + "\" does not exist and hence can't be removed.", context);
}
} else {
cerr << "Argument --remove-argument specified but no ID/name provided." << endl;
@ -107,13 +108,13 @@ void AttachmentInfo::apply(AbstractContainer *container)
}
}
void AttachmentInfo::apply(AbstractAttachment *attachment)
void AttachmentInfo::apply(AbstractAttachment *attachment, Media::Diagnostics &diag)
{
if(hasId) {
attachment->setId(id);
}
if(path) {
attachment->setFile(path);
attachment->setFile(path, diag);
}
if(name) {
attachment->setName(name);
@ -134,13 +135,13 @@ void AttachmentInfo::reset()
path = name = mime = desc = nullptr;
}
bool AttachmentInfo::next(AbstractContainer *container)
bool AttachmentInfo::next(AbstractContainer *container, Media::Diagnostics &diag)
{
if(!id && !path && !name && !mime && !desc) {
// skip empty attachment infos
return false;
}
apply(container);
apply(container, diag);
reset();
return true;
}

View File

@ -1,6 +1,8 @@
#ifndef CLI_ATTACHMENT_INFO
#define CLI_ATTACHMENT_INFO
#include <tagparser/diagnostics.h>
#include <c++utilities/conversion/types.h>
namespace Media {
@ -21,10 +23,10 @@ class AttachmentInfo
public:
AttachmentInfo();
void parseDenotation(const char *denotation);
void apply(Media::AbstractContainer *container);
void apply(Media::AbstractAttachment *attachment);
void apply(Media::AbstractContainer *container, Media::Diagnostics &diag);
void apply(Media::AbstractAttachment *attachment, Media::Diagnostics &diag);
void reset();
bool next(Media::AbstractContainer *container);
bool next(Media::AbstractContainer *container, Media::Diagnostics &diag);
AttachmentAction action;
uint64 id;

View File

@ -2,6 +2,8 @@
#include "./fieldmapping.h"
#include <tagparser/mediafileinfo.h>
#include <tagparser/diagnostics.h>
#include <tagparser/progressfeedback.h>
#include <tagparser/matroska/matroskatag.h>
#include <tagparser/mp4/mp4tag.h>
#include <tagparser/vorbis/vorbiscomment.h>
@ -118,16 +120,16 @@ string incremented(const string &str, unsigned int toIncrement)
return res;
}
void printNotifications(NotificationList &notifications, const char *head, bool beVerbose)
void printDiagMessages(const Diagnostics &diag, const char *head, bool beVerbose)
{
if(notifications.empty()) {
if(diag.empty()) {
return;
}
if(!beVerbose) {
for(const auto &notification : notifications) {
switch(notification.type()) {
case NotificationType::Debug:
case NotificationType::Information:
for(const auto &message : diag) {
switch(message.level()) {
case DiagLevel::Debug:
case DiagLevel::Information:
break;
default:
goto printNotifications;
@ -140,45 +142,37 @@ printNotifications:
if(head) {
cout << " - " << head << endl;
}
Notification::sortByTime(notifications);
for(const auto &notification : notifications) {
switch(notification.type()) {
case NotificationType::Debug:
for(const auto &message : diag) {
switch(message.level()) {
case DiagLevel::Debug:
if(beVerbose) {
cout << " Debug ";
break;
} else {
continue;
}
case NotificationType::Information:
case DiagLevel::Information:
if(beVerbose) {
cout << " Information ";
break;
} else {
continue;
}
case NotificationType::Warning:
case DiagLevel::Warning:
cout << " Warning ";
break;
case NotificationType::Critical:
case DiagLevel::Critical:
cout << " Error ";
break;
default:
;
}
cout << notification.creationTime().toString(DateTimeOutputFormat::TimeOnly) << " ";
cout << notification.context() << ": ";
cout << notification.message() << '\n';
cout << message.creationTime().toString(DateTimeOutputFormat::TimeOnly) << " ";
cout << message.context() << ": ";
cout << message.message() << '\n';
}
}
void printNotifications(const MediaFileInfo &fileInfo, const char *head, bool beVerbose)
{
NotificationList notifications;
fileInfo.gatherRelatedNotifications(notifications);
printNotifications(notifications, head, beVerbose);
}
void printProperty(const char *propName, const char *value, const char *suffix, Indentation indentation)
{
if(!*value) {
@ -658,29 +652,23 @@ bool stringToBool(const string &str)
}
bool logLineFinalized = true;
static string lastLoggedStatus;
void logStatus(const StatusProvider &statusProvider)
static string lastStep;
void logNextStep(const AbortableProgressFeedback &progress)
{
if(statusProvider.currentStatus() != lastLoggedStatus) {
// the ongoing operation ("status") has changed
// -> finalize previous line and make new line
if(!logLineFinalized) {
cout << "\r - [100%] " << lastLoggedStatus << endl;
logLineFinalized = true;
}
// -> update lastStatus
lastLoggedStatus = statusProvider.currentStatus();
// finalize previous step
if(!logLineFinalized) {
cout << "\r - [100%] " << lastStep << endl;
logLineFinalized = true;
}
// print line for next step
lastStep = progress.step();
cout << "\r - [" << setw(3) << static_cast<unsigned int>(progress.stepPercentage()) << "%] " << lastStep << flush;
logLineFinalized = false;
}
// update current line if an operation is ongoing (status is not empty)
if(!lastLoggedStatus.empty()) {
int percentage = static_cast<int>(statusProvider.currentPercentage() * 100);
if(percentage < 0) {
percentage = 0;
}
cout << "\r - [" << setw(3) << percentage << "%] " << lastLoggedStatus << flush;
logLineFinalized = false;
}
void logStepPercentage(const Media::AbortableProgressFeedback &progress)
{
cout << "\r - [" << setw(3) << static_cast<unsigned int>(progress.stepPercentage()) << "%] " << lastStep << flush;
}
void finalizeLog()
@ -688,9 +676,9 @@ void finalizeLog()
if(logLineFinalized) {
return;
}
cout << '\n';
cout << "\r - [100%] " << lastStep << '\n';
logLineFinalized = true;
lastLoggedStatus.clear();
lastStep.clear();
}
}

View File

@ -22,6 +22,8 @@ class Argument;
namespace Media {
class MediaFileInfo;
class Diagnostics;
class AbortableProgressFeedback;
enum class TagUsage;
enum class ElementPosition;
}
@ -263,8 +265,7 @@ constexpr bool isDigit(char c)
std::string incremented(const std::string &str, unsigned int toIncrement = 1);
void printNotifications(NotificationList &notifications, const char *head = nullptr, bool beVerbose = false);
void printNotifications(const MediaFileInfo &fileInfo, const char *head = nullptr, bool beVerbose = false);
void printDiagMessages(const Media::Diagnostics &diag, const char *head = nullptr, bool beVerbose = false);
void printProperty(const char *propName, const char *value, const char *suffix = nullptr, ApplicationUtilities::Indentation indentation = 4);
void printProperty(const char *propName, ElementPosition elementPosition, const char *suffix = nullptr, ApplicationUtilities::Indentation indentation = 4);
@ -310,7 +311,8 @@ FieldDenotations parseFieldDenotations(const ApplicationUtilities::Argument &fie
std::string tagName(const Tag *tag);
bool stringToBool(const std::string &str);
extern bool logLineFinalized;
void logStatus(const StatusProvider &statusProvider);
void logNextStep(const Media::AbortableProgressFeedback &progress);
void logStepPercentage(const Media::AbortableProgressFeedback &progress);
void finalizeLog();
}

View File

@ -18,6 +18,8 @@
#include <tagparser/abstractattachment.h>
#include <tagparser/abstractchapter.h>
#include <tagparser/backuphelper.h>
#include <tagparser/diagnostics.h>
#include <tagparser/progressfeedback.h>
#ifdef TAGEDITOR_JSON_EXPORT
# include <reflective_rapidjson/json/reflector.h>
@ -107,18 +109,21 @@ void generateFileInfo(const ArgumentOccurrence &, const Argument &inputFileArg,
MediaFileInfo inputFileInfo(inputFileArg.values().front());
inputFileInfo.setForceFullParse(validateArg.isPresent());
inputFileInfo.open(true);
inputFileInfo.parseEverything();
Diagnostics diag;
inputFileInfo.parseEverything(diag);
// generate and save info
Diagnostics diagReparsing;
(outputFileArg.isPresent() ? cout : cerr) << "Saving file info for \"" << inputFileArg.values().front() << "\" ..." << endl;
NotificationList origNotify;
if(outputFileArg.isPresent()) {
QFile file(fromNativeFileName(outputFileArg.values().front()));
if(file.open(QFile::WriteOnly) && file.write(HtmlInfo::generateInfo(inputFileInfo, origNotify)) && file.flush()) {
cout << "File information has been saved to \"" << outputFileArg.values().front() << "\"." << endl;
} else {
cerr << Phrases::Error << "An IO error occured when writing the file \"" << outputFileArg.values().front() << "\"." << Phrases::EndFlush;
}
if(!outputFileArg.isPresent()) {
cout << HtmlInfo::generateInfo(inputFileInfo, diag, diagReparsing).data() << endl;
return;
}
QFile file(fromNativeFileName(outputFileArg.values().front()));
if(file.open(QFile::WriteOnly) && file.write(HtmlInfo::generateInfo(inputFileInfo, diag, diagReparsing)) && file.flush()) {
cout << "File information has been saved to \"" << outputFileArg.values().front() << "\"." << endl;
} else {
cout << HtmlInfo::generateInfo(inputFileInfo, origNotify).data() << endl;
cerr << Phrases::Error << "An IO error occured when writing the file \"" << outputFileArg.values().front() << "\"." << Phrases::EndFlush;
}
} catch(const Media::Failure &) {
cerr << Phrases::Error << "A parsing failure occured when reading the file \"" << inputFileArg.values().front() << "\"." << Phrases::EndFlush;
@ -146,14 +151,15 @@ void displayFileInfo(const ArgumentOccurrence &, const Argument &filesArg, const
MediaFileInfo fileInfo;
for(const char *file : filesArg.values()) {
Diagnostics diag;
try {
// parse tags
fileInfo.setPath(file);
fileInfo.open(true);
fileInfo.parseContainerFormat();
fileInfo.parseTracks();
fileInfo.parseAttachments();
fileInfo.parseChapters();
fileInfo.parseContainerFormat(diag);
fileInfo.parseTracks(diag);
fileInfo.parseAttachments(diag);
fileInfo.parseChapters(diag);
// print general/container-related info
cout << "Technical information for \"" << file << "\":\n";
@ -176,8 +182,8 @@ void displayFileInfo(const ArgumentOccurrence &, const Argument &filesArg, const
printProperty("Duration", container->duration());
printProperty("Creation time", container->creationTime());
printProperty("Modification time", container->modificationTime());
printProperty("Tag position", container->determineTagPosition());
printProperty("Index position", container->determineIndexPosition());
printProperty("Tag position", container->determineTagPosition(diag));
printProperty("Index position", container->determineIndexPosition(diag));
}
if(fileInfo.paddingSize()) {
printProperty("Padding", dataSizeToString(fileInfo.paddingSize()));
@ -295,7 +301,7 @@ void displayFileInfo(const ArgumentOccurrence &, const Argument &filesArg, const
cerr << Phrases::Error << "An IO failure occured when reading the file \"" << file << "\"." << Phrases::EndFlush;
}
printNotifications(fileInfo, "Parsing notifications:", verboseArg.isPresent());
printDiagMessages(diag, "Diagnostic messages:", verboseArg.isPresent());
cout << endl;
}
}
@ -315,12 +321,13 @@ void displayTagInfo(const Argument &fieldsArg, const Argument &filesArg, const A
MediaFileInfo fileInfo;
for(const char *file : filesArg.values()) {
Diagnostics diag;
try {
// parse tags
fileInfo.setPath(file);
fileInfo.open(true);
fileInfo.parseContainerFormat();
fileInfo.parseTags();
fileInfo.parseContainerFormat(diag);
fileInfo.parseTags(diag);
cout << "Tag information for \"" << file << "\":\n";
const auto tags = fileInfo.tags();
if(!tags.empty()) {
@ -353,7 +360,7 @@ void displayTagInfo(const Argument &fieldsArg, const Argument &filesArg, const A
::IoUtilities::catchIoFailure();
cerr << Phrases::Error << "An IO failure occured when reading the file \"" << file << "\"." << Phrases::EndFlush;
}
printNotifications(fileInfo, "Parsing notifications:", verboseArg.isPresent());
printDiagMessages(diag, "Diagnostic messages:", verboseArg.isPresent());
cout << endl;
}
}
@ -454,17 +461,16 @@ void setTagInfo(const SetTagInfoArgs &args)
// iterate through all specified files
unsigned int fileIndex = 0;
static const string context("setting tags");
NotificationList notifications;
static string context("setting tags");
for(const char *file : args.filesArg.values()) {
Diagnostics diag;
try {
// parse tags and tracks (tracks are relevent because track meta-data such as language can be changed as well)
cout << TextAttribute::Bold << "Setting tag information for \"" << file << "\" ..." << Phrases::EndFlush;
notifications.clear();
fileInfo.setPath(file);
fileInfo.parseContainerFormat();
fileInfo.parseTags();
fileInfo.parseTracks();
fileInfo.parseContainerFormat(diag);
fileInfo.parseTags(diag);
fileInfo.parseTracks(diag);
vector<Tag *> tags;
// remove tags with the specified targets
@ -517,7 +523,7 @@ void setTagInfo(const SetTagInfoArgs &args)
// alter tags
fileInfo.tags(tags);
if(tags.empty()) {
fileInfo.addNotification(NotificationType::Critical, "Can not create appropriate tags for file.", context);
diag.emplace_back(DiagLevel::Critical, "Can not create appropriate tags for file.", context);
} else {
// iterate through all tags
for(auto *tag : tags) {
@ -534,7 +540,7 @@ void setTagInfo(const SetTagInfoArgs &args)
if(!tag->canEncodingBeUsed(denotedEncoding)) {
usedEncoding = tag->proposedTextEncoding();
if(args.encodingArg.isPresent()) {
fileInfo.addNotification(NotificationType::Warning, argsToString("Can't use specified encoding \"", args.encodingArg.values().front(), "\" in ", tagName(tag), " because the tag format/version doesn't support it."), context);
diag.emplace_back(DiagLevel::Warning, argsToString("Can't use specified encoding \"", args.encodingArg.values().front(), "\" in ", tagName(tag), " because the tag format/version doesn't support it."), context);
}
}
// iterate through all denoted field values
@ -553,8 +559,9 @@ void setTagInfo(const SetTagInfoArgs &args)
try {
// assume the file refers to a picture
MediaFileInfo fileInfo(relevantDenotedValue->value);
Diagnostics diag;
fileInfo.open(true);
fileInfo.parseContainerFormat();
fileInfo.parseContainerFormat(diag);
auto buff = make_unique<char []>(fileInfo.size());
fileInfo.stream().seekg(0);
fileInfo.stream().read(buff.get(), fileInfo.size());
@ -562,10 +569,10 @@ void setTagInfo(const SetTagInfoArgs &args)
value.setMimeType(fileInfo.mimeType());
convertedValues.emplace_back(move(value));
} catch(const Media::Failure &) {
fileInfo.addNotification(NotificationType::Critical, "Unable to parse specified cover file.", context);
diag.emplace_back(DiagLevel::Critical, "Unable to parse specified cover file.", context);
} catch(...) {
::IoUtilities::catchIoFailure();
fileInfo.addNotification(NotificationType::Critical, "An IO error occured when parsing the specified cover file.", context);
diag.emplace_back(DiagLevel::Critical, "An IO error occured when parsing the specified cover file.", context);
}
} else {
convertedValues.emplace_back(relevantDenotedValue->value, TagTextEncoding::Utf8, usedEncoding);
@ -579,7 +586,7 @@ void setTagInfo(const SetTagInfoArgs &args)
try {
denotedScope.field.setValues(tag, tagType, convertedValues);
} catch(const ConversionException &e) {
fileInfo.addNotification(NotificationType::Critical, argsToString("Unable to parse denoted field ID \"", denotedScope.field.name(), "\": ", e.what()), context);
diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse denoted field ID \"", denotedScope.field.name(), "\": ", e.what()), context);
}
}
}
@ -613,10 +620,10 @@ void setTagInfo(const SetTagInfoArgs &args)
} else if(field.denotes("default")) {
track->setDefault(stringToBool(value));
} else {
fileInfo.addNotification(NotificationType::Critical, argsToString("Denoted track property name \"", field.denotation(), "\" is invalid"), argsToString("setting meta-data of track ", track->id()));
diag.emplace_back(DiagLevel::Critical, argsToString("Denoted track property name \"", field.denotation(), "\" is invalid"), argsToString("setting meta-data of track ", track->id()));
}
} catch(const ConversionException &e) {
fileInfo.addNotification(NotificationType::Critical, argsToString("Unable to parse value for track property \"", field.denotation(), "\": ", e.what()), argsToString("setting meta-data of track ", track->id()));
diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse value for track property \"", field.denotation(), "\": ", e.what()), argsToString("setting meta-data of track ", track->id()));
}
}
}
@ -635,7 +642,7 @@ void setTagInfo(const SetTagInfoArgs &args)
bool attachmentsModified = false;
if(args.addAttachmentArg.isPresent() || args.updateAttachmentArg.isPresent() || args.removeAttachmentArg.isPresent() || args.removeExistingAttachmentsArg.isPresent()) {
static const string context("setting attachments");
fileInfo.parseAttachments();
fileInfo.parseAttachments(diag);
if(fileInfo.attachmentsParsingStatus() == ParsingStatus::Ok) {
if(container) {
// ignore all existing attachments if argument is specified
@ -652,24 +659,24 @@ void setTagInfo(const SetTagInfoArgs &args)
for(const char *value : args.addAttachmentArg.values(i)) {
currentInfo.parseDenotation(value);
}
attachmentsModified |= currentInfo.next(container);
attachmentsModified |= currentInfo.next(container, diag);
}
currentInfo.action = AttachmentAction::Update;
for(size_t i = 0, occurrences = args.updateAttachmentArg.occurrences(); i != occurrences; ++i) {
for(const char *value : args.updateAttachmentArg.values(i)) {
currentInfo.parseDenotation(value);
}
attachmentsModified |= currentInfo.next(container);
attachmentsModified |= currentInfo.next(container, diag);
}
currentInfo.action = AttachmentAction::Remove;
for(size_t i = 0, occurrences = args.removeAttachmentArg.occurrences(); i != occurrences; ++i) {
for(const char *value : args.removeAttachmentArg.values(i)) {
currentInfo.parseDenotation(value);
}
attachmentsModified |= currentInfo.next(container);
attachmentsModified |= currentInfo.next(container, diag);
}
} else {
fileInfo.addNotification(NotificationType::Critical, "Unable to assign attachments because the container object has not been initialized.", context);
diag.emplace_back(DiagLevel::Critical, "Unable to assign attachments because the container object has not been initialized.", context);
}
} else {
// notification will be added by the file info automatically
@ -677,22 +684,14 @@ void setTagInfo(const SetTagInfoArgs &args)
}
// apply changes
fileInfo.setSaveFilePath(currentOutputFile != noMoreOutputFiles ? string(*currentOutputFile) : string());
try {
// save parsing notifications because notifications of sub objects like tags, tracks, ... will be gone after applying changes
fileInfo.setSaveFilePath(currentOutputFile != noMoreOutputFiles ? string(*currentOutputFile) : string());
fileInfo.gatherRelatedNotifications(notifications);
fileInfo.invalidateNotifications();
// create handler for progress updates and aborting
AbortableProgressFeedback progress(logNextStep, logStepPercentage);
const InterruptHandler handler(bind(&AbortableProgressFeedback::tryToAbort, ref(progress)));
// register handler for logging status
fileInfo.registerCallback(logStatus);
// register interrupt handler
const InterruptHandler handler([&fileInfo] {
fileInfo.tryToAbort();
});
// apply changes and gather notifications
fileInfo.applyChanges();
// apply changes
fileInfo.applyChanges(diag, progress);
// notify about completion
finalizeLog();
@ -714,8 +713,7 @@ void setTagInfo(const SetTagInfoArgs &args)
cerr << " - " << Phrases::Error << "An IO failure occured when reading/writing the file \"" << file << "\"." << Phrases::EndFlush;
}
fileInfo.gatherRelatedNotifications(notifications);
printNotifications(notifications, "Notifications:", args.verboseArg.isPresent());
printDiagMessages(diag, "Diagnostic messages:", args.verboseArg.isPresent());
// continue with next file
++fileIndex;
@ -747,6 +745,7 @@ void extractField(const Argument &fieldArg, const Argument &attachmentArg, const
MediaFileInfo inputFileInfo;
for(const char *file : inputFilesArg.values()) {
Diagnostics diag;
try {
// setup media file info
inputFileInfo.setPath(file);
@ -756,8 +755,8 @@ void extractField(const Argument &fieldArg, const Argument &attachmentArg, const
if(!fieldDenotations.empty()) {
// extract tag field
(outputFileArg.isPresent() ? cout : cerr) << "Extracting field " << fieldArg.values().front() << " of \"" << file << "\" ..." << endl;
inputFileInfo.parseContainerFormat();
inputFileInfo.parseTags();
inputFileInfo.parseContainerFormat(diag);
inputFileInfo.parseTags(diag);
auto tags = inputFileInfo.tags();
vector<pair<const TagValue *, string> > values;
// iterate through all tags
@ -769,7 +768,7 @@ void extractField(const Argument &fieldArg, const Argument &attachmentArg, const
values.emplace_back(value, joinStrings({tag->typeName(), numberToString(values.size())}, "-", true));
}
} catch(const ConversionException &e) {
inputFileInfo.addNotification(NotificationType::Critical, "Unable to parse denoted field ID \"" % string(fieldDenotation.first.field.name()) % "\": " + e.what(), "extracting field");
diag.emplace_back(DiagLevel::Critical, argsToString("Unable to parse denoted field ID \"", fieldDenotation.first.field.name(), "\": ", e.what()), "extracting field");
}
}
}
@ -812,8 +811,8 @@ void extractField(const Argument &fieldArg, const Argument &attachmentArg, const
}
logStream << " of \"" << file << "\" ..." << endl;
inputFileInfo.parseContainerFormat();
inputFileInfo.parseAttachments();
inputFileInfo.parseContainerFormat(diag);
inputFileInfo.parseAttachments(diag);
vector<pair<const AbstractAttachment *, string> > attachments;
// iterate through all attachments
for(const AbstractAttachment *attachment : inputFileInfo.attachments()) {
@ -856,7 +855,7 @@ void extractField(const Argument &fieldArg, const Argument &attachmentArg, const
::IoUtilities::catchIoFailure();
cerr << Phrases::Error << "An IO failure occured when reading the file \"" << file << "\"." << Phrases::End;
}
printNotifications(inputFileInfo, "Parsing notifications:", verboseArg.isPresent());
printDiagMessages(diag, "Diagnostic messages:", verboseArg.isPresent());
}
}
@ -877,13 +876,14 @@ void exportToJson(const ArgumentOccurrence &, const Argument &filesArg, const Ar
// gather tags for each file
for(const char *file : filesArg.values()) {
Diagnostics diag;
try {
// parse tags
fileInfo.setPath(file);
fileInfo.open(true);
fileInfo.parseContainerFormat();
fileInfo.parseTags();
fileInfo.parseTracks();
fileInfo.parseContainerFormat(diag);
fileInfo.parseTags(diag);
fileInfo.parseTracks(diag);
jsonData.emplace_back(fileInfo, document.GetAllocator());
} catch(const Media::Failure &) {
cerr << Phrases::Error << "A parsing failure occured when reading the file \"" << file << "\"." << Phrases::EndFlush;
@ -893,6 +893,8 @@ void exportToJson(const ArgumentOccurrence &, const Argument &filesArg, const Ar
}
}
// TODO: serialize diag messages
// print the gathered data as JSON document
ReflectiveRapidJSON::JsonReflector::push(jsonData, document, document.GetAllocator());
RAPIDJSON_NAMESPACE::OStreamWrapper osw(cout);

View File

@ -6,6 +6,7 @@
#include <tagparser/mediafileinfo.h>
#include <tagparser/abstractattachment.h>
#include <tagparser/diagnostics.h>
#include <qtutilities/misc/conversion.h>
@ -91,16 +92,17 @@ void AttachmentsEdit::invalidate()
void AttachmentsEdit::addFile(const QString &path)
{
if(fileInfo() && fileInfo()->attachmentsParsingStatus() == ParsingStatus::Ok && fileInfo()->container()) {
// create and add attachment
auto *attachment = fileInfo()->container()->createAttachment();
attachment->setIgnored(true);
attachment->setFile(toNativeFileName(path).data());
m_addedAttachments << attachment;
m_model->addAttachment(-1, attachment, true, path);
} else {
if(!fileInfo() || fileInfo()->attachmentsParsingStatus() != ParsingStatus::Ok || !fileInfo()->container()) {
throw Failure();
}
// create and add attachment
auto *const attachment = fileInfo()->container()->createAttachment();
attachment->setIgnored(true);
Diagnostics diag;
attachment->setFile(toNativeFileName(path).data(), diag);
// TODO: show diag messages
m_addedAttachments << attachment;
m_model->addAttachment(-1, attachment, true, path);
}
void AttachmentsEdit::showFileSelection()

View File

@ -11,7 +11,7 @@
#include <tagparser/mp4/mp4container.h>
#include <tagparser/abstracttrack.h>
#include <tagparser/abstractattachment.h>
#include <tagparser/notification.h>
#include <tagparser/diagnostics.h>
#include <c++utilities/chrono/datetime.h>
#include <c++utilities/conversion/stringconversion.h>
@ -30,6 +30,7 @@ using namespace std;
using namespace ChronoUtilities;
using namespace ConversionUtilities;
using namespace Media;
using namespace Utility;
namespace QtGui {
@ -115,34 +116,33 @@ private:
QStandardItem *m_item;
};
void addNotifications(Media::NotificationList *notifications, QStandardItem *parent)
void addDiagMessages(Media::Diagnostics *diag, QStandardItem *parent)
{
Notification::sortByTime(*notifications);
for(Notification &notification : *notifications) {
for(const auto &msg : *diag) {
QList<QStandardItem *> notificationRow;
notificationRow.reserve(3);
auto *firstItem = defaultItem(QString::fromUtf8(notification.creationTime().toString().data()));
switch(notification.type()) {
case NotificationType::None:
break;
case NotificationType::Debug:
auto *firstItem = defaultItem(QString::fromUtf8(msg.creationTime().toString().data()));
switch(msg.level()) {
case DiagLevel::None:
case DiagLevel::Debug:
firstItem->setIcon(FileInfoModel::debugIcon());
break;
case NotificationType::Information:
case DiagLevel::Information:
firstItem->setIcon(FileInfoModel::informationIcon());
break;
case NotificationType::Warning:
case DiagLevel::Warning:
firstItem->setIcon(FileInfoModel::warningIcon());
break;
case NotificationType::Critical:
case DiagLevel::Critical:
case DiagLevel::Fatal:
firstItem->setIcon(FileInfoModel::errorIcon());
break;
}
parent->appendRow(QList<QStandardItem *>()
<< firstItem
<< defaultItem(QString::fromUtf8(notification.message().data()))
<< defaultItem(QString::fromUtf8(notification.context().data())));
<< defaultItem(QString::fromUtf8(msg.message().data()))
<< defaultItem(QString::fromUtf8(msg.context().data())));
}
}
@ -190,10 +190,9 @@ template<class ElementType, bool isAdditional = false> void addElementNode(const
/*!
* \brief Constructs a new instance with the specified \a fileInfo which might be nullptr.
*/
FileInfoModel::FileInfoModel(Media::MediaFileInfo *fileInfo, QObject *parent) :
FileInfoModel::FileInfoModel(QObject *parent) :
QStandardItemModel(parent)
{
setFileInfo(fileInfo);
}
QVariant FileInfoModel::headerData(int section, Qt::Orientation orientation, int role) const
@ -231,10 +230,11 @@ const MediaFileInfo *FileInfoModel::fileInfo() const
* \brief Assigns a Media::MediaFileInfo.
* \remarks Causes updating the internal cache and resets the model.
*/
void FileInfoModel::setFileInfo(MediaFileInfo *fileInfo, Media::NotificationList *originalNotifications)
void FileInfoModel::setFileInfo(MediaFileInfo &fileInfo, Diagnostics &diag, Diagnostics *diagReparsing)
{
m_file = fileInfo;
m_originalNotifications = originalNotifications;
m_file = &fileInfo;
m_diag = &diag;
m_diagReparsing = diagReparsing;
updateCache();
}
@ -271,6 +271,9 @@ void FileInfoModel::updateCache()
beginResetModel();
clear();
if(m_file) {
// get diag
Diagnostics &diag = m_diagReparsing ? *m_diagReparsing : *m_diag;
// get container
AbstractContainer *container = m_file->container();
@ -327,8 +330,8 @@ void FileInfoModel::updateCache()
containerHelper.appendRow(tr("Document type"), container->documentType());
containerHelper.appendRow(tr("Document version"), container->doctypeVersion());
containerHelper.appendRow(tr("Document read version"), container->doctypeReadVersion());
containerHelper.appendRow(tr("Tag position"), Utility::elementPositionToQString(container->determineTagPosition()));
containerHelper.appendRow(tr("Index position"), Utility::elementPositionToQString(container->determineIndexPosition()));
containerHelper.appendRow(tr("Tag position"), Utility::elementPositionToQString(container->determineTagPosition(diag)));
containerHelper.appendRow(tr("Index position"), Utility::elementPositionToQString(container->determineIndexPosition(diag)));
}
containerHelper.appendRow(tr("Padding size"), m_file->paddingSize());
@ -338,7 +341,7 @@ void FileInfoModel::updateCache()
if(!tags.empty()) {
auto *tagsItem = defaultItem(tr("Tags"));
setItem(++currentRow, tagsItem);
setItem(currentRow, 1, defaultItem(tr("%1 tag(s) assigned", nullptr, static_cast<int>(tags.size())).arg(tags.size())));
setItem(currentRow, 1, defaultItem(tr("%1 tag(s) assigned", nullptr, trQuandity(tags.size())).arg(tags.size())));
for(const Tag *tag : tags) {
auto *tagItem = defaultItem(tag->typeName());
@ -360,9 +363,9 @@ void FileInfoModel::updateCache()
setItem(++currentRow, tracksItem);
const string summary(m_file->technicalSummary());
if(summary.empty()) {
setItem(currentRow, 1, defaultItem(tr("%1 track(s) contained", nullptr, static_cast<int>(tracks.size())).arg(tracks.size())));
setItem(currentRow, 1, defaultItem(tr("%1 track(s) contained", nullptr, trQuandity(tracks.size())).arg(tracks.size())));
} else {
setItem(currentRow, 1, defaultItem(tr("%1 track(s): ", nullptr, static_cast<int>(tracks.size())).arg(tracks.size()) + QString::fromUtf8(summary.data(), summary.size())));
setItem(currentRow, 1, defaultItem(tr("%1 track(s): ", nullptr, trQuandity(tracks.size())).arg(tracks.size()) + QString::fromUtf8(summary.data(), summary.size())));
}
size_t number = 0;
@ -461,7 +464,7 @@ void FileInfoModel::updateCache()
if(!attachments.empty()) {
auto *attachmentsItem = defaultItem(tr("Attachments"));
setItem(++currentRow, attachmentsItem);
setItem(currentRow, 1, defaultItem(tr("%1 attachment(s) present", nullptr, attachments.size()).arg(attachments.size())));
setItem(currentRow, 1, defaultItem(tr("%1 attachment(s) present", nullptr, trQuandity(attachments.size())).arg(attachments.size())));
size_t number = 0;
for(const AbstractAttachment *attachment : attachments) {
@ -524,7 +527,7 @@ void FileInfoModel::updateCache()
if(!editionEntries.empty()) {
auto *editionsItem = defaultItem(tr("Editions"));
setItem(++currentRow, editionsItem);
setItem(currentRow, 1, defaultItem(tr("%1 edition(s) present", nullptr, editionEntries.size()).arg(editionEntries.size())));
setItem(currentRow, 1, defaultItem(tr("%1 edition(s) present", nullptr, trQuandity(editionEntries.size())).arg(editionEntries.size())));
size_t editionNumber = 0;
for(const auto &edition : editionEntries) {
auto *editionItem = defaultItem(tr("Edition #%1").arg(++editionNumber));
@ -554,7 +557,7 @@ void FileInfoModel::updateCache()
if(!chapters.empty()) {
auto *chaptersItem = defaultItem(tr("Chapters"));
setItem(++currentRow, chaptersItem);
setItem(currentRow, 1, defaultItem(tr("%1 chapter(s) present", nullptr, chapters.size()).arg(chapters.size())));
setItem(currentRow, 1, defaultItem(tr("%1 chapter(s) present", nullptr, trQuandity(chapters.size())).arg(chapters.size())));
for(const AbstractChapter *chapter : chapters) {
addChapter(chapter, chaptersItem);
}
@ -592,18 +595,14 @@ void FileInfoModel::updateCache()
}
// notifications
auto currentNotifications = m_file->gatherRelatedNotifications();
if(!currentNotifications.empty()) {
auto *notificationsItem= defaultItem(m_originalNotifications ? tr("Notifications (reparsing after saving)") : tr("Notifications"));
addNotifications(&currentNotifications, notificationsItem);
setItem(++currentRow, notificationsItem);
auto *const diagItem = defaultItem(tr("Diagnostic messages"));
addDiagMessages(m_diag, diagItem);
setItem(++currentRow, diagItem);
if (m_diagReparsing) {
auto *diagReparsingItem = defaultItem(tr("Diagnostic messages from reparsing"));
addDiagMessages(m_diagReparsing, diagReparsingItem);
setItem(++currentRow, diagReparsingItem);
}
if(m_originalNotifications && !m_originalNotifications->empty()) {
auto *notificationsItem = defaultItem(tr("Notifications"));
addNotifications(m_originalNotifications, notificationsItem);
setItem(++currentRow, notificationsItem);
}
}
endResetModel();
}

View File

@ -7,8 +7,7 @@
namespace Media {
class MediaFileInfo;
class Notification;
typedef std::list<Notification> NotificationList;
class Diagnostics;
}
namespace QtGui {
@ -17,12 +16,12 @@ class FileInfoModel : public QStandardItemModel
{
Q_OBJECT
public:
explicit FileInfoModel(Media::MediaFileInfo *fileInfo = nullptr, QObject *parent = nullptr);
explicit FileInfoModel(QObject *parent = nullptr);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
const Media::MediaFileInfo *fileInfo() const;
void setFileInfo(Media::MediaFileInfo *fileInfo, Media::NotificationList *originalNotifications = nullptr);
void setFileInfo(Media::MediaFileInfo &fileInfo, Media::Diagnostics &diag, Media::Diagnostics *diagReparsing = nullptr);
#if defined(GUI_QTWIDGETS)
static const QIcon &informationIcon();
@ -36,7 +35,8 @@ private:
private:
Media::MediaFileInfo *m_file;
Media::NotificationList *m_originalNotifications;
Media::Diagnostics *m_diag;
Media::Diagnostics *m_diagReparsing;
};
}

View File

@ -1,5 +1,7 @@
#include "./notificationmodel.h"
#include "../misc/utility.h"
#include <c++utilities/chrono/datetime.h>
#include <QApplication>
@ -9,14 +11,15 @@
using namespace std;
using namespace ChronoUtilities;
using namespace Media;
using namespace Utility;
namespace QtGui {
NotificationModel::NotificationModel(QObject *parent) :
DiagModel::DiagModel(QObject *parent) :
QAbstractListModel(parent)
{}
QVariant NotificationModel::headerData(int section, Qt::Orientation orientation, int role) const
QVariant DiagModel::headerData(int section, Qt::Orientation orientation, int role) const
{
switch(orientation) {
case Qt::Horizontal:
@ -41,7 +44,7 @@ QVariant NotificationModel::headerData(int section, Qt::Orientation orientation,
return QVariant();
}
int NotificationModel::columnCount(const QModelIndex &parent) const
int DiagModel::columnCount(const QModelIndex &parent) const
{
if(!parent.isValid()) {
return 3;
@ -49,27 +52,27 @@ int NotificationModel::columnCount(const QModelIndex &parent) const
return 0;
}
int NotificationModel::rowCount(const QModelIndex &parent) const
int DiagModel::rowCount(const QModelIndex &parent) const
{
if(!parent.isValid()) {
return m_notifications.size();
return sizeToInt(m_diag.size());
}
return 0;
}
Qt::ItemFlags NotificationModel::flags(const QModelIndex &index) const
Qt::ItemFlags DiagModel::flags(const QModelIndex &index) const
{
return QAbstractListModel::flags(index);
}
QVariant NotificationModel::data(const QModelIndex &index, int role) const
QVariant DiagModel::data(const QModelIndex &index, int role) const
{
if(index.isValid() && index.row() < m_notifications.size()) {
if(index.isValid() && index.row() >= 0 && static_cast<std::size_t>(index.row()) < m_diag.size()) {
switch(role) {
case Qt::DisplayRole:
switch(index.column()) {
case 0: {
const string &context = m_notifications.at(index.row()).context();
const string &context = m_diag[static_cast<std::size_t>(index.row())].context();
if(context.empty()) {
return tr("unspecified");
} else {
@ -77,9 +80,9 @@ QVariant NotificationModel::data(const QModelIndex &index, int role) const
}
}
case 1:
return QString::fromUtf8(m_notifications.at(index.row()).message().c_str());
return QString::fromUtf8(m_diag[static_cast<std::size_t>(index.row())].message().c_str());
case 2:
return QString::fromUtf8(m_notifications.at(index.row()).creationTime().toString(DateTimeOutputFormat::DateAndTime, true).c_str());
return QString::fromUtf8(m_diag[static_cast<std::size_t>(index.row())].creationTime().toString(DateTimeOutputFormat::DateAndTime, true).c_str());
default:
;
}
@ -87,18 +90,19 @@ QVariant NotificationModel::data(const QModelIndex &index, int role) const
case Qt::DecorationRole:
switch(index.column()) {
case 0:
switch(m_notifications.at(index.row()).type()) {
case NotificationType::Information:
return informationIcon();
case NotificationType::Warning:
return warningIcon();
case NotificationType::Critical:
return errorIcon();
case NotificationType::Debug:
switch(m_diag[static_cast<std::size_t>(index.row())].level()) {
case DiagLevel::None:
case DiagLevel::Debug:
return debugIcon();
default:
;
case DiagLevel::Information:
return informationIcon();
case DiagLevel::Warning:
return warningIcon();
case DiagLevel::Critical:
case DiagLevel::Fatal:
return errorIcon();
}
break;
default:
;
}
@ -110,42 +114,37 @@ QVariant NotificationModel::data(const QModelIndex &index, int role) const
return QVariant();
}
const QList<Notification> &NotificationModel::notifications() const
const Diagnostics &DiagModel::diagnostics() const
{
return m_notifications;
return m_diag;
}
void NotificationModel::setNotifications(const QList<Notification> &notifications)
void DiagModel::setDiagnostics(const Media::Diagnostics &notifications)
{
beginResetModel();
m_notifications = notifications;
m_diag = notifications;
endResetModel();
}
void NotificationModel::setNotifications(const NotificationList &notifications)
{
setNotifications(QList<Notification>::fromStdList(notifications));
}
const QIcon &NotificationModel::informationIcon()
const QIcon &DiagModel::informationIcon()
{
static const QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation);
return icon;
}
const QIcon &NotificationModel::warningIcon()
const QIcon &DiagModel::warningIcon()
{
static const QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning);
return icon;
}
const QIcon &NotificationModel::errorIcon()
const QIcon &DiagModel::errorIcon()
{
static const QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxCritical);
return icon;
}
const QIcon &NotificationModel::debugIcon()
const QIcon &DiagModel::debugIcon()
{
static const QIcon icon = QIcon(QStringLiteral("/images/bug"));
return icon;

View File

@ -1,17 +1,17 @@
#ifndef NOTIFICATIONMODEL_H
#define NOTIFICATIONMODEL_H
#include <tagparser/notification.h>
#include <tagparser/diagnostics.h>
#include <QAbstractListModel>
namespace QtGui {
class NotificationModel : public QAbstractListModel
class DiagModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit NotificationModel(QObject *parent = nullptr);
explicit DiagModel(QObject *parent = nullptr);
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
int columnCount(const QModelIndex &parent) const;
@ -19,9 +19,8 @@ public:
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
const QList<Media::Notification> &notifications() const;
void setNotifications(const QList<Media::Notification> &notifications);
void setNotifications(const Media::NotificationList &notifications);
const Media::Diagnostics &diagnostics() const;
void setDiagnostics(const Media::Diagnostics &diagnostics);
static const QIcon &informationIcon();
static const QIcon &warningIcon();
@ -33,7 +32,7 @@ signals:
public slots:
private:
QList<Media::Notification> m_notifications;
Media::Diagnostics m_diag;
};

View File

@ -11,6 +11,7 @@
#include <tagparser/id3/id3v2frame.h>
#include <tagparser/vorbis/vorbiscomment.h>
#include <tagparser/vorbis/vorbiscommentfield.h>
#include <tagparser/diagnostics.h>
#include <qtutilities/misc/conversion.h>
@ -318,8 +319,10 @@ void PicturePreviewSelection::addOfSelectedType(const QString &path)
TagValue &selectedCover = m_values[m_currentTypeIndex];
try {
MediaFileInfo fileInfo(toNativeFileName(path).constData());
Diagnostics diag;
fileInfo.open(true);
fileInfo.parseContainerFormat();
fileInfo.parseContainerFormat(diag);
// TODO: show diagnostic messages
auto mimeType = QString::fromUtf8(fileInfo.mimeType());
bool ok;
mimeType = QInputDialog::getText(this, tr("Enter/confirm mime type"), tr("Confirm or enter the mime type of the selected file."), QLineEdit::Normal, mimeType, &ok);

View File

@ -164,7 +164,7 @@ TagEditorWidget::~TagEditorWidget()
const QByteArray &TagEditorWidget::generateFileInfoHtml()
{
if(m_fileInfoHtml.isEmpty()) {
m_fileInfoHtml = HtmlInfo::generateInfo(m_fileInfo, m_originalNotifications);
m_fileInfoHtml = HtmlInfo::generateInfo(m_fileInfo, m_diag, m_diagReparsing);
}
return m_fileInfoHtml;
}
@ -627,7 +627,8 @@ void TagEditorWidget::initInfoView()
m_ui->tagSplitter->addWidget(m_infoTreeView);
}
if(!m_infoModel) {
m_infoModel = new FileInfoModel(m_fileInfo.isOpen() ? &m_fileInfo : nullptr, this);
m_infoModel = new FileInfoModel(this);
m_infoModel->setFileInfo(m_fileInfo, m_diag, m_makingResultsAvailable ? &m_diagReparsing : nullptr);
m_infoTreeView->setModel(m_infoModel);
m_infoTreeView->setHeaderHidden(true);
m_infoTreeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
@ -658,7 +659,7 @@ void TagEditorWidget::updateInfoView()
// update info model if present
if(m_infoModel) {
m_infoModel->setFileInfo(m_fileInfo.isOpen() ? &m_fileInfo : nullptr); // resets the model
m_infoModel->setFileInfo(m_fileInfo, m_diag, m_makingResultsAvailable ? &m_diagReparsing : nullptr); // resets the model
}
}
@ -758,8 +759,6 @@ bool TagEditorWidget::startParsing(const QString &path, bool forceRefresh)
// clear previous results and status
m_tags.clear();
m_fileInfo.clearParsingResults();
m_fileInfo.invalidateStatus();
m_fileInfo.invalidateNotifications();
if(!sameFile) {
// close last file if possibly open
m_fileInfo.close();
@ -768,25 +767,26 @@ bool TagEditorWidget::startParsing(const QString &path, bool forceRefresh)
m_fileInfo.setSaveFilePath(string());
m_fileInfo.setPath(toNativeFileName(path).data());
// update file name and directory
QFileInfo fileInfo(path);
const QFileInfo fileInfo(path);
m_lastDir = m_currentDir;
m_currentDir = fileInfo.absolutePath();
m_fileName = fileInfo.fileName();
}
// update availability of making results
// write diagnostics to m_diagReparsing if making results are avalable
m_makingResultsAvailable &= sameFile;
if(!m_makingResultsAvailable) {
m_originalNotifications.clear();
}
Diagnostics &diag = m_makingResultsAvailable ? m_diagReparsing : m_diag;
// clear diagnostics
diag.clear();
m_diagReparsing.clear();
// show filename
m_ui->fileNameLabel->setText(m_fileName);
// define function to parse the file
auto startThread = [this] {
const auto startThread = [this, &diag] {
char result;
try { // credits for this nesting go to GCC regression 66145
try {
// try to open with write access
try {
// try to open with write access
m_fileInfo.reopen(false);
} catch(...) {
::IoUtilities::catchIoFailure();
@ -794,7 +794,7 @@ bool TagEditorWidget::startParsing(const QString &path, bool forceRefresh)
m_fileInfo.reopen(true);
}
m_fileInfo.setForceFullParse(Settings::values().editor.forceFullParse);
m_fileInfo.parseEverything();
m_fileInfo.parseEverything(diag);
result = ParsingSuccessful;
} catch(const Failure &) {
// the file has been opened; parsing notifications will be shown in the info box
@ -806,21 +806,19 @@ bool TagEditorWidget::startParsing(const QString &path, bool forceRefresh)
result = IoError;
}
} catch(const exception &e) {
m_fileInfo.addNotification(Media::NotificationType::Critical, string("Something completely unexpected happened: ") + e.what(), "parsing");
diag.emplace_back(Media::DiagLevel::Critical, argsToString("Something completely unexpected happened: ", + e.what()), "parsing");
result = FatalParsingError;
} catch(...) {
m_fileInfo.addNotification(Media::NotificationType::Critical, "Something completely unexpected happened", "parsing");
diag.emplace_back(Media::DiagLevel::Critical, "Something completely unexpected happened", "parsing");
result = FatalParsingError;
}
m_fileInfo.unregisterAllCallbacks();
QMetaObject::invokeMethod(this, "showFile", Qt::QueuedConnection, Q_ARG(char, result));
};
m_fileInfo.unregisterAllCallbacks();
m_fileOperationOngoing = true;
// perform the operation concurrently
QtConcurrent::run(startThread);
// inform user
static const QString statusMsg(tr("The file is beeing parsed ..."));
static const auto statusMsg(tr("The file is beeing parsed ..."));
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Progress);
m_ui->parsingNotificationWidget->setText(statusMsg);
m_ui->parsingNotificationWidget->setVisible(true); // ensure widget is visible!
@ -899,10 +897,13 @@ void TagEditorWidget::showFile(char result)
}
// show parsing status/result using parsing notification widget
auto worstNotificationType = m_fileInfo.worstNotificationTypeIncludingRelatedObjects();
if(worstNotificationType >= Media::NotificationType::Critical) {
// we catched no exception, but there are critical notifications
// -> treat critical notifications as fatal parsing errors
auto diagLevel = m_diag.level();
if (diagLevel < worstDiagLevel) {
diagLevel |= m_diagReparsing.level();
}
if(diagLevel >= DiagLevel::Critical) {
// we catched no exception, but there are critical diag messages
// -> treat those as fatal parsing errors
result = LoadingResult::FatalParsingError;
}
switch(result) {
@ -915,12 +916,12 @@ void TagEditorWidget::showFile(char result)
m_ui->parsingNotificationWidget->setText(tr("File couldn't be parsed correctly."));
}
bool multipleSegmentsNotTested = m_fileInfo.containerFormat() == ContainerFormat::Matroska && m_fileInfo.container()->segmentCount() > 1;
if(worstNotificationType >= Media::NotificationType::Critical) {
if(diagLevel >= Media::DiagLevel::Critical) {
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Critical);
m_ui->parsingNotificationWidget->appendLine(tr("There are critical parsing notifications."));
} else if(worstNotificationType == Media::NotificationType::Warning || m_fileInfo.isReadOnly() || !m_fileInfo.areTagsSupported() || multipleSegmentsNotTested) {
m_ui->parsingNotificationWidget->appendLine(tr("Errors occured."));
} else if(diagLevel == Media::DiagLevel::Warning || m_fileInfo.isReadOnly() || !m_fileInfo.areTagsSupported() || multipleSegmentsNotTested) {
m_ui->parsingNotificationWidget->setNotificationType(NotificationType::Warning);
if(worstNotificationType == Media::NotificationType::Warning) {
if(diagLevel == Media::DiagLevel::Warning) {
m_ui->parsingNotificationWidget->appendLine(tr("There are warnings."));
}
}
@ -1099,21 +1100,26 @@ bool TagEditorWidget::startSaving()
m_fileInfo.setMinPadding(settings.minPadding);
m_fileInfo.setMaxPadding(settings.maxPadding);
m_fileInfo.setPreferredPadding(settings.preferredPadding);
// define functions to show the saving progress and to actually applying the changes
auto showProgress = [this] (StatusProvider &sender) -> void {
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setPercentage", Qt::QueuedConnection, Q_ARG(int, static_cast<int>(sender.currentPercentage() * 100.0)));
if(m_abortClicked) {
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, tr("Cancelling ...")));
m_fileInfo.tryToAbort();
} else {
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(sender.currentStatus())));
}
};
auto startThread = [this] {
const auto startThread = [this] {
// define functions to show the saving progress and to actually applying the changes
const auto showPercentage([this] (const AbortableProgressFeedback &progress) {
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setPercentage", Qt::QueuedConnection, Q_ARG(int, progress.stepPercentage()));
});
const auto showStep([this] (AbortableProgressFeedback &progress) {
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setPercentage", Qt::QueuedConnection, Q_ARG(int, progress.stepPercentage()));
if(m_abortClicked) {
progress.tryToAbort();
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, tr("Cancelling ...")));
} else {
QMetaObject::invokeMethod(m_ui->makingNotificationWidget, "setText", Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(progress.step())));
}
});
AbortableProgressFeedback progress(std::move(showStep), std::move(showPercentage));
bool processingError = false, ioError = false;
try {
try {
m_fileInfo.applyChanges();
m_fileInfo.applyChanges(m_diag, progress);
} catch(const Failure &) {
processingError = true;
} catch(...) {
@ -1121,17 +1127,14 @@ bool TagEditorWidget::startSaving()
ioError = true;
}
} catch(const exception &e) {
m_fileInfo.addNotification(Media::NotificationType::Critical, string("Something completely unexpected happened: ") + e.what(), "making");
m_diag.emplace_back(Media::DiagLevel::Critical, argsToString("Something completely unexpected happened: ", e.what()), "making");
processingError = true;
} catch(...) {
m_fileInfo.addNotification(Media::NotificationType::Critical, "Something completely unexpected happened", "making");
m_diag.emplace_back(Media::DiagLevel::Critical, "Something completely unexpected happened", "making");
processingError = true;
}
m_fileInfo.unregisterAllCallbacks();
QMetaObject::invokeMethod(this, "showSavingResult", Qt::QueuedConnection, Q_ARG(bool, processingError), Q_ARG(bool, ioError));
};
m_fileInfo.unregisterAllCallbacks();
m_fileInfo.registerCallback(showProgress);
m_fileOperationOngoing = true;
// use another thread to perform the operation
QtConcurrent::run(startThread);
@ -1155,17 +1158,17 @@ void TagEditorWidget::showSavingResult(bool processingError, bool ioError)
m_ui->makingNotificationWidget->setPercentage(-1);
m_ui->makingNotificationWidget->setHidden(false);
m_makingResultsAvailable = true;
m_originalNotifications = m_fileInfo.gatherRelatedNotifications();
if(!processingError && !ioError) {
// display status messages
QString statusMsg;
size_t critical = 0, warnings = 0;
for(const Notification &notification : m_originalNotifications) {
switch(notification.type()) {
case Media::NotificationType::Critical:
for(const auto &msg : m_diag) {
switch(msg.level()) {
case Media::DiagLevel::Fatal:
case Media::DiagLevel::Critical:
++critical;
break;
case Media::NotificationType::Warning:
case Media::DiagLevel::Warning:
++warnings;
break;
default:
@ -1174,12 +1177,12 @@ void TagEditorWidget::showSavingResult(bool processingError, bool ioError)
}
if(warnings || critical) {
if(critical) {
statusMsg = tr("The tags have been saved, but there is/are %1 warning(s) ", 0, warnings).arg(warnings);
statusMsg.append(tr("and %1 error(s).", 0, critical).arg(critical));
statusMsg = tr("The tags have been saved, but there is/are %1 warning(s) ", nullptr, trQuandity(warnings)).arg(warnings);
statusMsg.append(tr("and %1 error(s).", nullptr, trQuandity(critical)).arg(critical));
} else {
statusMsg = tr("The tags have been saved, but there is/are %1 warning(s).", 0, warnings).arg(warnings);
statusMsg = tr("The tags have been saved, but there is/are %1 warning(s).", nullptr, trQuandity(warnings)).arg(warnings);
}
m_ui->makingNotificationWidget->setNotificationType(critical > 0 ? NotificationType::Critical : NotificationType::Warning);
m_ui->makingNotificationWidget->setNotificationType(critical ? NotificationType::Critical : NotificationType::Warning);
} else {
statusMsg = tr("The tags have been saved.");

View File

@ -5,6 +5,7 @@
#include "./webviewdefs.h"
#include <tagparser/mediafileinfo.h>
#include <tagparser/diagnostics.h>
#include <QWidget>
#include <QByteArray>
@ -49,7 +50,7 @@ public:
const QString &currentPath() const;
const QString &currentDir() const;
Media::MediaFileInfo &fileInfo();
Media::NotificationList &originalNotifications();
const Media::Diagnostics &diagnostics() const;
bool isTagEditShown() const;
const QByteArray &fileInfoHtml() const;
const QByteArray &generateFileInfoHtml();
@ -150,9 +151,10 @@ private:
QString m_lastDir;
QString m_saveFilePath;
// status
Media::Diagnostics m_diag;
Media::Diagnostics m_diagReparsing;
bool m_nextFileAfterSaving;
bool m_makingResultsAvailable;
Media::NotificationList m_originalNotifications;
bool m_abortClicked;
bool m_fileOperationOngoing;
};
@ -191,11 +193,11 @@ inline Media::MediaFileInfo &TagEditorWidget::fileInfo()
}
/*!
* \brief Returns the original notifications.
* \brief Returns the diagnostic messages.
*/
inline Media::NotificationList &TagEditorWidget::originalNotifications()
inline const Media::Diagnostics &TagEditorWidget::diagnostics() const
{
return m_originalNotifications;
return m_diag;
}
/*!

View File

@ -9,7 +9,6 @@
#include <tagparser/mp4/mp4container.h>
#include <tagparser/abstracttrack.h>
#include <tagparser/abstractattachment.h>
#include <tagparser/notification.h>
#include <qtutilities/resources/resources.h>
@ -56,6 +55,7 @@ using namespace std;
using namespace ConversionUtilities;
using namespace ChronoUtilities;
using namespace Media;
using namespace Utility;
namespace HtmlInfo {
@ -308,11 +308,12 @@ class Generator
{
public:
Generator(const MediaFileInfo &file, NotificationList &originalNotifications) :
Generator(const MediaFileInfo &file, Diagnostics &diag, Diagnostics &diagReparsing) :
m_writer(&m_res),
m_rowMaker(m_writer),
m_file(file),
originalNotifications(originalNotifications)
m_diag(diag),
m_diagReparsing(diagReparsing)
{}
QString mkStyle()
@ -808,33 +809,33 @@ public:
}
}
void mkNotifications(NotificationList &notifications, bool reparsing = false)
void mkNotifications(Diagnostics &diag, bool reparsing = false)
{
if(notifications.size()) {
startTableSection();
const QString moreId(reparsing ? QStringLiteral("notificationsReparsingMore") : QStringLiteral("notificationsMore"));
m_rowMaker.startRow(reparsing ? QCoreApplication::translate("HtmlInfo", "Notifications (reparsing after saving)") : QCoreApplication::translate("HtmlInfo", "Notifications"));
m_writer.writeCharacters(QCoreApplication::translate("HtmlInfo", "%1 notification(s) available", 0, static_cast<int>(notifications.size())).arg(notifications.size()));
mkSpace();
mkDetailsLink(moreId, QCoreApplication::translate("HtmlInfo", "show notifications"));
m_rowMaker.endRow();
m_writer.writeEndElement();
if (diag.empty()) {
return;
}
startTableSection();
const QString moreId(reparsing ? QStringLiteral("notificationsReparsingMore") : QStringLiteral("notificationsMore"));
m_rowMaker.startRow(reparsing ? QCoreApplication::translate("HtmlInfo", "Notifications (reparsing after saving)") : QCoreApplication::translate("HtmlInfo", "Notifications"));
m_writer.writeCharacters(QCoreApplication::translate("HtmlInfo", "%1 notification(s) available", nullptr, trQuandity(diag.size())).arg(diag.size()));
mkSpace();
mkDetailsLink(moreId, QCoreApplication::translate("HtmlInfo", "show notifications"));
m_rowMaker.endRow();
m_writer.writeEndElement();
startExtendedTableSection(moreId);
m_rowMaker.startHorizontalSubTab(QString(), QStringList() << QString() << QCoreApplication::translate("HtmlInfo", "Context") << QCoreApplication::translate("HtmlInfo", "Message") << QCoreApplication::translate("HtmlInfo", "Time"));
Notification::sortByTime(notifications);
for(const Notification &notification : notifications) {
m_writer.writeStartElement(QStringLiteral("tr"));
m_writer.writeEmptyElement(QStringLiteral("td"));
m_writer.writeAttribute(QStringLiteral("class"), qstr(notification.typeName()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(notification.context()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(notification.message()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(notification.creationTime().toString(DateTimeOutputFormat::DateAndTime, false)));
m_writer.writeEndElement();
}
m_rowMaker.endSubTab();
startExtendedTableSection(moreId);
m_rowMaker.startHorizontalSubTab(QString(), QStringList({QString(), QCoreApplication::translate("HtmlInfo", "Context"), QCoreApplication::translate("HtmlInfo", "Message"), QCoreApplication::translate("HtmlInfo", "Time")}));
for(const auto &msg : diag) {
m_writer.writeStartElement(QStringLiteral("tr"));
m_writer.writeEmptyElement(QStringLiteral("td"));
m_writer.writeAttribute(QStringLiteral("class"), qstr(msg.levelName()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(msg.context()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(msg.message()));
m_writer.writeTextElement(QStringLiteral("td"), qstr(msg.creationTime().toString(DateTimeOutputFormat::DateAndTime, false)));
m_writer.writeEndElement();
}
m_rowMaker.endSubTab();
m_writer.writeEndElement();
}
void mkDoc()
@ -932,8 +933,8 @@ public:
if(m_file.paddingSize()) {
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Padding size"), QStringLiteral("%1 (%2 %)").arg(qstr(dataSizeToString(m_file.paddingSize(), true))).arg(static_cast<double>(m_file.paddingSize()) / m_file.size() * 100.0, 0, 'g', 2));
}
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Tag position"), container->determineTagPosition());
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Index position"), container->determineIndexPosition());
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Tag position"), container->determineTagPosition(m_diagReparsing));
rowMaker.mkRow(QCoreApplication::translate("HtmlInfo", "Index position"), container->determineIndexPosition(m_diagReparsing));
m_writer.writeEndElement();
}
@ -1110,11 +1111,8 @@ public:
}
// notifications
auto currentNotifications = m_file.gatherRelatedNotifications();
mkNotifications(currentNotifications, !originalNotifications.empty());
if(!originalNotifications.empty()) {
mkNotifications(originalNotifications);
}
mkNotifications(m_diag);
mkNotifications(m_diagReparsing, true);
// </table> </body> </html>
@ -1132,7 +1130,8 @@ private:
QByteArray m_res;
RowMaker m_rowMaker;
const MediaFileInfo &m_file;
NotificationList &originalNotifications;
Diagnostics &m_diag;
Diagnostics &m_diagReparsing;
};
/*!
@ -1143,9 +1142,9 @@ private:
* A QGuiApplication instance should be available for setting fonts.
* A QApplication instance should be available for standard icons.
*/
QByteArray generateInfo(const MediaFileInfo &file, NotificationList &originalNotifications)
QByteArray generateInfo(const MediaFileInfo &file, Diagnostics &diag, Diagnostics &diagReparsing)
{
Generator gen(file, originalNotifications);
Generator gen(file, diag, diagReparsing);
gen.mkDoc();
#ifdef QT_DEBUG
QFile test(QStringLiteral("/tmp/test.xhtml"));

View File

@ -7,13 +7,12 @@
namespace Media {
class MediaFileInfo;
class Notification;
typedef std::list<Notification> NotificationList;
class Diagnostics;
}
namespace HtmlInfo {
QByteArray generateInfo(const Media::MediaFileInfo &file, Media::NotificationList &originalNotifications);
QByteArray generateInfo(const Media::MediaFileInfo &file, Media::Diagnostics &diag, Media::Diagnostics &diagReparsing);
}

View File

@ -30,6 +30,16 @@ void parseFileName(const QString &fileName, QString &title, int &trackNumber);
QString printModel(QAbstractItemModel *model);
void printModelIndex(const QModelIndex &index, QString &res, int level);
constexpr int sizeToInt(std::size_t size)
{
return size > std::numeric_limits<int>::max() ? std::numeric_limits<int>::max() : static_cast<int>(size);
}
constexpr int trQuandity(quint64 quandity)
{
return quandity > std::numeric_limits<int>::max() ? std::numeric_limits<int>::max() : static_cast<int>(quandity);
}
}
#endif // UTILITYFEATURES_H

View File

@ -29,17 +29,17 @@ using namespace std;
namespace RenamingUtility {
/// \brief Adds notifications from \a statusProvider to \a notificationsObject.
TAGEDITOR_JS_VALUE &operator <<(TAGEDITOR_JS_VALUE &notificationsObject, const StatusProvider &statusProvider)
TAGEDITOR_JS_VALUE &operator <<(TAGEDITOR_JS_VALUE &diagObject, const Diagnostics &diag)
{
quint32 counter = 0;
for(const auto &notification : statusProvider.notifications()) {
for(const auto &msg : diag) {
TAGEDITOR_JS_VALUE val;
val.setProperty("msg", QString::fromUtf8(notification.message().data()) TAGEDITOR_JS_READONLY);
val.setProperty("critical", notification.type() == NotificationType::Critical TAGEDITOR_JS_READONLY);
notificationsObject.setProperty(counter, val);
val.setProperty("msg", QString::fromUtf8(msg.message().data()) TAGEDITOR_JS_READONLY);
val.setProperty("critical", msg.level() >= DiagLevel::Critical TAGEDITOR_JS_READONLY);
diagObject.setProperty(counter, val);
++counter;
}
return notificationsObject;
return diagObject;
}
/// \brief Add fields and notifications from \a tag to \a tagObject.
@ -79,8 +79,6 @@ TAGEDITOR_JS_VALUE &operator <<(TAGEDITOR_JS_VALUE &tagObject, const Tag &tag)
tagObject.setProperty("diskPos", pos.position() TAGEDITOR_JS_READONLY);
tagObject.setProperty("diskTotal", pos.total() TAGEDITOR_JS_READONLY);
// add notifications
tagObject.setProperty("hasCriticalNotifications", tag.hasCriticalNotifications() TAGEDITOR_JS_READONLY);
return tagObject;
}
@ -138,6 +136,7 @@ const QString &TagEditorObject::newRelativeDirectory() const
TAGEDITOR_JS_VALUE TagEditorObject::parseFileInfo(const QString &fileName)
{
Diagnostics diag;
MediaFileInfo fileInfo(toNativeFileName(fileName).data());
// add basic file information
@ -153,7 +152,7 @@ TAGEDITOR_JS_VALUE TagEditorObject::parseFileInfo(const QString &fileName)
// parse further file information
bool criticalParseingErrorOccured = false;
try {
fileInfo.parseEverything();
fileInfo.parseEverything(diag);
} catch(const Failure &) {
// parsing notifications will be addded anyways
criticalParseingErrorOccured = true;
@ -163,11 +162,11 @@ TAGEDITOR_JS_VALUE TagEditorObject::parseFileInfo(const QString &fileName)
}
// gather notifications
auto mainNotificationObject = m_engine->newArray(static_cast<uint>(fileInfo.notifications().size()));
mainNotificationObject << fileInfo;
criticalParseingErrorOccured |= fileInfo.hasCriticalNotifications();
fileInfoObject.setProperty(QStringLiteral("hasCriticalNotifications"), criticalParseingErrorOccured);
fileInfoObject.setProperty(QStringLiteral("notifications"), mainNotificationObject);
auto diagObj = m_engine->newArray(static_cast<uint>(diag.size()));
diagObj << diag;
criticalParseingErrorOccured |= diag.level() >= DiagLevel::Critical;
fileInfoObject.setProperty(QStringLiteral("hasCriticalMessages"), criticalParseingErrorOccured);
fileInfoObject.setProperty(QStringLiteral("diagMessages"), diagObj);
// add MIME-type, suitable suffix and technical summary
fileInfoObject.setProperty(QStringLiteral("mimeType"), QString::fromUtf8(fileInfo.mimeType()) TAGEDITOR_JS_READONLY);
@ -186,9 +185,6 @@ TAGEDITOR_JS_VALUE TagEditorObject::parseFileInfo(const QString &fileName)
combinedTagObject << tag;
combinedTagNotifications << tag;
tagObject << tag;
auto tagNotificationsObject = m_engine->newArray(static_cast<uint>(tag.notifications().size()));
tagNotificationsObject << tag;
tagObject.setProperty(QStringLiteral("notifications"), tagNotificationsObject TAGEDITOR_JS_READONLY);
tagsObject.setProperty(tagIndex, tagObject TAGEDITOR_JS_READONLY);
}
combinedTagObject.setProperty(QStringLiteral("notifications"), combinedTagNotifications TAGEDITOR_JS_READONLY);
@ -206,9 +202,6 @@ TAGEDITOR_JS_VALUE TagEditorObject::parseFileInfo(const QString &fileName)
trackObject.setProperty(QStringLiteral("format"), QString::fromUtf8(track.formatName()));
trackObject.setProperty(QStringLiteral("formatAbbreviation"), QString::fromUtf8(track.formatAbbreviation()));
trackObject.setProperty(QStringLiteral("description"), QString::fromUtf8(track.description().data()));
auto trackNotificationsObject = m_engine->newArray(static_cast<uint>(track.notifications().size()));
trackNotificationsObject << track;
trackObject.setProperty(QStringLiteral("notifications"), trackNotificationsObject TAGEDITOR_JS_READONLY);
tracksObject.setProperty(trackIndex, trackObject TAGEDITOR_JS_READONLY);
}
@ -228,12 +221,12 @@ TAGEDITOR_JS_VALUE TagEditorObject::parseFileName(const QString &fileName)
TAGEDITOR_JS_VALUE TagEditorObject::allFiles(const QString &dirName)
{
QDir dir(dirName);
const QDir dir(dirName);
if(dir.exists()) {
QStringList files = dir.entryList(QDir::Files);
auto entriesObj = m_engine->newArray(files.length());
const auto files(dir.entryList(QDir::Files));
auto entriesObj = m_engine->newArray(static_cast<uint>(files.size()));
quint32 counter = 0;
for(const QString &file : files) {
for(const auto &file : files) {
entriesObj.setProperty(counter, file TAGEDITOR_JS_READONLY);
++counter;
}
@ -245,9 +238,9 @@ TAGEDITOR_JS_VALUE TagEditorObject::allFiles(const QString &dirName)
TAGEDITOR_JS_VALUE TagEditorObject::firstFile(const QString &dirName)
{
QDir dir(dirName);
const QDir dir(dirName);
if(dir.exists()) {
QStringList files = dir.entryList(QDir::Files);
const auto files(dir.entryList(QDir::Files));
if(!files.empty()) {
return TAGEDITOR_JS_VALUE(files.first());
}

View File

@ -2,9 +2,27 @@
#include <c++utilities/io/catchiofailure.h>
#include <c++utilities/io/misc.h>
#include <c++utilities/io/path.h>
#include <c++utilities/tests/testutils.h>
// order of includes and definition of operator << matters for C++ to resolve the correct overload
#include <tagparser/mediafileinfo.h>
#include <tagparser/diagnostics.h>
namespace TestUtilities {
/*!
* \brief Prints a DiagMessage to enable using it in CPPUNIT_ASSERT_EQUAL.
*/
inline std::ostream &operator <<(std::ostream &os, const Media::DiagMessage &diagMessage)
{
return os << diagMessage.levelName() << ':' << ' ' << diagMessage.message() << ' ' << '(' << diagMessage.context() << ')';
}
}
using namespace TestUtilities;
#include <c++utilities/tests/testutils.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestFixture.h>
@ -804,9 +822,10 @@ void CliTests::testExtraction()
// test extraction of cover
const char *const args1[] = {"tageditor", "extract", "cover", "-f", mp4File1.data(), "-o", "/tmp/extracted.jpeg", nullptr};
TESTUTILS_ASSERT_EXEC(args1);
Diagnostics diag;
MediaFileInfo extractedInfo("/tmp/extracted.jpeg");
extractedInfo.open(true);
extractedInfo.parseContainerFormat();
extractedInfo.parseContainerFormat(diag);
CPPUNIT_ASSERT_EQUAL(22771_st, extractedInfo.size());
CPPUNIT_ASSERT(ContainerFormat::Jpeg == extractedInfo.containerFormat());
extractedInfo.invalidate();
@ -819,12 +838,13 @@ void CliTests::testExtraction()
CPPUNIT_ASSERT_EQUAL(0, remove("/tmp/extracted.jpeg"));
TESTUTILS_ASSERT_EXEC(args3);
extractedInfo.open(true);
extractedInfo.parseContainerFormat();
extractedInfo.parseContainerFormat(diag);
CPPUNIT_ASSERT_EQUAL(22771_st, extractedInfo.size());
CPPUNIT_ASSERT(ContainerFormat::Jpeg == extractedInfo.containerFormat());
CPPUNIT_ASSERT_EQUAL(0, remove("/tmp/extracted.jpeg"));
CPPUNIT_ASSERT_EQUAL(0, remove(mp4File2.data()));
CPPUNIT_ASSERT_EQUAL(0, remove((mp4File2 + ".bak").data()));
CPPUNIT_ASSERT_EQUAL(Diagnostics(), diag);
}
/*!