Adapt to new notification handling
This commit is contained in:
parent
254b9b7661
commit
c2b2e4ac44
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ¬ifications, 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 ¬ification : 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 ¬ification : 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 ¬ifications, 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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 ¬ification : *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(¤tNotifications, 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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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> ¬ifications)
|
||||
void DiagModel::setDiagnostics(const Media::Diagnostics ¬ifications)
|
||||
{
|
||||
beginResetModel();
|
||||
m_notifications = notifications;
|
||||
m_diag = notifications;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void NotificationModel::setNotifications(const NotificationList ¬ifications)
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -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> ¬ifications() const;
|
||||
void setNotifications(const QList<Media::Notification> ¬ifications);
|
||||
void setNotifications(const Media::NotificationList ¬ifications);
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ¬ification : 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.");
|
||||
|
|
|
@ -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 ¤tPath() const;
|
||||
const QString ¤tDir() 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;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -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 ¬ifications, 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 ¬ification : 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"));
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ¬ificationsObject, const StatusProvider &statusProvider)
|
||||
TAGEDITOR_JS_VALUE &operator <<(TAGEDITOR_JS_VALUE &diagObject, const Diagnostics &diag)
|
||||
{
|
||||
quint32 counter = 0;
|
||||
for(const auto ¬ification : 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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
Loading…
Reference in New Issue