2015-09-06 20:20:00 +02:00
# include "./mainfeatures.h"
2017-01-15 21:43:46 +01:00
# include "./helper.h"
# include "./attachmentinfo.h"
2018-01-15 00:14:53 +01:00
# ifdef TAGEDITOR_JSON_EXPORT
# include "./json.h"
# endif
2015-09-06 15:41:17 +02:00
2015-09-06 20:20:00 +02:00
# include "../application/knownfieldmodel.h"
2017-04-27 22:10:55 +02:00
# if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
2016-07-27 18:31:42 +02:00
# include ".. / misc / utility.h"
# include ".. / misc / htmlinfo.h"
# endif
2015-04-22 19:33:53 +02:00
# include <tagparser/mediafileinfo.h>
# include <tagparser/tag.h>
2017-01-15 21:43:46 +01:00
# include <tagparser/tagvalue.h>
2015-08-09 23:53:45 +02:00
# include <tagparser/abstracttrack.h>
# include <tagparser/abstractattachment.h>
# include <tagparser/abstractchapter.h>
2015-04-22 19:33:53 +02:00
2018-01-21 15:27:34 +01:00
# ifdef TAGEDITOR_JSON_EXPORT
# include <reflective_rapidjson / json / reflector.h>
# endif
2018-01-15 00:14:53 +01:00
2015-04-22 19:33:53 +02:00
# include <c++utilities/application/failure.h>
2015-09-01 20:20:15 +02:00
# include <c++utilities/application/commandlineutils.h>
2015-04-22 19:33:53 +02:00
# include <c++utilities/conversion/stringconversion.h>
2017-01-30 22:13:49 +01:00
# include <c++utilities/conversion/stringbuilder.h>
2015-04-22 19:33:53 +02:00
# include <c++utilities/conversion/conversionexception.h>
# include <c++utilities/io/ansiescapecodes.h>
2016-06-14 22:54:49 +02:00
# include <c++utilities/io/catchiofailure.h>
2016-12-19 23:53:12 +01:00
# include <c++utilities/io/nativefilestream.h>
2015-04-22 19:33:53 +02:00
2017-04-27 22:10:55 +02:00
# if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
2016-07-27 18:31:42 +02:00
# include <QDir>
2016-12-30 05:39:03 +01:00
# include <qtutilities / misc / conversion.h>
2016-07-27 18:31:42 +02:00
# endif
2015-04-22 19:33:53 +02:00
2018-01-15 00:14:53 +01:00
# ifdef TAGEDITOR_JSON_EXPORT
# include <rapidjson/ostreamwrapper.h>
# include <rapidjson/writer.h>
2018-01-26 18:09:53 +01:00
# include <rapidjson/prettywriter.h>
2018-01-15 00:14:53 +01:00
# endif
2015-04-22 19:33:53 +02:00
# include <iostream>
2017-09-22 00:19:24 +02:00
# include <iomanip>
2016-07-27 18:31:42 +02:00
# include <cstring>
# include <algorithm>
2017-02-05 21:04:27 +01:00
# include <memory>
2015-04-22 19:33:53 +02:00
using namespace std ;
using namespace ApplicationUtilities ;
using namespace ConversionUtilities ;
using namespace ChronoUtilities ;
2016-12-19 23:53:12 +01:00
using namespace IoUtilities ;
2015-04-22 19:33:53 +02:00
using namespace EscapeCodes ;
using namespace Settings ;
using namespace Media ;
2017-04-27 22:10:55 +02:00
# if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
2016-07-27 18:31:42 +02:00
using namespace Utility ;
# endif
2015-04-22 19:33:53 +02:00
namespace Cli {
2017-01-23 00:27:21 +01:00
# define FIELD_NAMES \
2017-12-05 15:21:59 +01:00
" title album artist genre year comment bpm bps lyricist disk part totalparts encoder \n " \
" recorddate performers duration language encodersettings lyrics synchronizedlyrics grouping \n " \
" recordlabel cover composer rating description "
# define TRACK_ATTRIBUTE_NAMES \
" name tracknumber enabled=yes enabled=no forced=yes forced=no default=yes default=no "
2016-07-04 23:28:11 +02:00
2017-01-23 00:27:21 +01:00
# define TAG_MODIFIER \
" tag=id3v1 tag=id3v2 tag=id3 tag=itunes tag=vorbis tag=matroska tag=all "
2017-12-05 15:21:59 +01:00
# define TRACK_MODIFIER \
" track= track=all "
2017-01-23 00:27:21 +01:00
# define TARGET_MODIFIER \
" target-level target-levelname target-tracks target-tracks \n " \
2017-12-05 15:21:59 +01:00
" target-chapters target-editions target-attachments target-reset "
2016-07-31 23:22:22 +02:00
const char * const fieldNames = FIELD_NAMES ;
2017-12-05 15:21:59 +01:00
const char * const fieldNamesForSet = TAG_MODIFIER " " FIELD_NAMES " " TRACK_MODIFIER " " TRACK_ATTRIBUTE_NAMES " " TARGET_MODIFIER ;
2016-07-31 23:22:22 +02:00
2017-10-09 20:34:08 +02:00
void printFieldNames ( const ArgumentOccurrence & )
2015-04-22 19:33:53 +02:00
{
2015-09-01 20:20:15 +02:00
CMD_UTILS_START_CONSOLE ;
2017-10-09 20:34:08 +02:00
2017-12-05 15:21:59 +01:00
cout < < " Field and track attribute names allow referring to a field or track attribute in a format-independent way. \n "
" - Field names: \n " FIELD_NAMES " \n "
" - Track attribute names: " TRACK_ATTRIBUTE_NAMES " \n \n "
" Modifier specify to which tags and tracks the subsequent values should be applied. \n "
" - Tag modifier: " TAG_MODIFIER " \n "
" - Track modifier: track=id1,id2,id3,... track=all \n "
" - Target modifier: \n " TARGET_MODIFIER " \n " < < flush ;
2015-04-22 19:33:53 +02:00
}
2016-07-31 23:22:22 +02:00
void generateFileInfo ( const ArgumentOccurrence & , const Argument & inputFileArg , const Argument & outputFileArg , const Argument & validateArg )
2015-04-22 19:33:53 +02:00
{
2015-09-01 20:20:15 +02:00
CMD_UTILS_START_CONSOLE ;
2017-10-09 20:34:08 +02:00
2017-04-27 22:10:55 +02:00
# if defined(TAGEDITOR_GUI_QTWIDGETS) || defined(TAGEDITOR_GUI_QTQUICK)
2015-04-22 19:33:53 +02:00
try {
// parse tags
MediaFileInfo inputFileInfo ( inputFileArg . values ( ) . front ( ) ) ;
inputFileInfo . setForceFullParse ( validateArg . isPresent ( ) ) ;
inputFileInfo . open ( true ) ;
inputFileInfo . parseEverything ( ) ;
2016-08-03 17:48:53 +02:00
( outputFileArg . isPresent ( ) ? cout : cerr ) < < " Saving file info for \" " < < inputFileArg . values ( ) . front ( ) < < " \" ... " < < endl ;
2015-04-22 19:33:53 +02:00
NotificationList origNotify ;
2016-08-03 17:48:53 +02:00
if ( outputFileArg . isPresent ( ) ) {
2016-12-20 23:53:33 +01:00
QFile file ( fromNativeFileName ( outputFileArg . values ( ) . front ( ) ) ) ;
2016-08-03 17:48:53 +02:00
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 {
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " An IO error occured when writing the file \" " < < outputFileArg . values ( ) . front ( ) < < " \" . " < < Phrases : : EndFlush ;
2016-08-03 17:48:53 +02:00
}
2015-04-22 19:33:53 +02:00
} else {
2016-08-03 17:48:53 +02:00
cout < < HtmlInfo : : generateInfo ( inputFileInfo , origNotify ) . data ( ) < < endl ;
2015-04-22 19:33:53 +02:00
}
2017-10-09 19:01:49 +02:00
} catch ( const Media : : Failure & ) {
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " A parsing failure occured when reading the file \" " < < inputFileArg . values ( ) . front ( ) < < " \" . " < < Phrases : : EndFlush ;
2016-06-14 22:54:49 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " An IO failure occured when reading the file \" " < < inputFileArg . values ( ) . front ( ) < < " \" . " < < Phrases : : EndFlush ;
2015-04-22 19:33:53 +02:00
}
2016-07-27 18:31:42 +02:00
# else
VAR_UNUSED ( inputFileArg ) ;
VAR_UNUSED ( outputFileArg ) ;
VAR_UNUSED ( validateArg ) ;
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " Generating HTML info is only available if built with Qt support. " < < Phrases : : EndFlush ;
2016-07-27 18:31:42 +02:00
# endif
2015-04-22 19:33:53 +02:00
}
2016-07-31 23:22:22 +02:00
void displayFileInfo ( const ArgumentOccurrence & , const Argument & filesArg , const Argument & verboseArg )
2015-08-09 23:53:45 +02:00
{
2015-09-01 20:20:15 +02:00
CMD_UTILS_START_CONSOLE ;
2017-10-09 20:34:08 +02:00
// check whether files have been specified
2016-06-25 23:11:16 +02:00
if ( ! filesArg . isPresent ( ) | | filesArg . values ( ) . empty ( ) ) {
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Error < < " No files have been specified. " < < Phrases : : End ;
2015-08-09 23:53:45 +02:00
return ;
}
2017-10-09 20:34:08 +02:00
2015-08-09 23:53:45 +02:00
MediaFileInfo fileInfo ;
2016-08-03 17:48:53 +02:00
for ( const char * file : filesArg . values ( ) ) {
2015-08-09 23:53:45 +02:00
try {
// parse tags
fileInfo . setPath ( file ) ;
fileInfo . open ( true ) ;
2016-08-03 17:48:53 +02:00
fileInfo . parseContainerFormat ( ) ;
2015-08-09 23:53:45 +02:00
fileInfo . parseTracks ( ) ;
fileInfo . parseAttachments ( ) ;
fileInfo . parseChapters ( ) ;
2017-09-22 00:19:24 +02:00
// print general/container-related info
cout < < " Technical information for \" " < < file < < " \" : \n " ;
cout < < " - " < < TextAttribute : : Bold < < " Container format: " < < fileInfo . containerFormatName ( ) < < Phrases : : End ;
if ( const auto container = fileInfo . container ( ) ) {
size_t segmentIndex = 0 ;
for ( const auto & title : container - > titles ( ) ) {
if ( segmentIndex ) {
printProperty ( " Title " , title % " (segment " % + + segmentIndex + " ) " ) ;
} else {
+ + segmentIndex ;
printProperty ( " Title " , title ) ;
2015-10-14 19:49:48 +02:00
}
}
2017-09-22 00:19:24 +02:00
printProperty ( " Document type " , container - > documentType ( ) ) ;
printProperty ( " Read version " , container - > readVersion ( ) ) ;
printProperty ( " Version " , container - > version ( ) ) ;
printProperty ( " Document read version " , container - > doctypeReadVersion ( ) ) ;
printProperty ( " Document version " , container - > doctypeVersion ( ) ) ;
printProperty ( " Duration " , container - > duration ( ) ) ;
printProperty ( " Creation time " , container - > creationTime ( ) ) ;
printProperty ( " Modification time " , container - > modificationTime ( ) ) ;
printProperty ( " Tag position " , container - > determineTagPosition ( ) ) ;
printProperty ( " Index position " , container - > determineIndexPosition ( ) ) ;
2015-10-14 19:49:48 +02:00
}
2017-09-22 00:19:24 +02:00
if ( fileInfo . paddingSize ( ) ) {
printProperty ( " Padding " , dataSizeToString ( fileInfo . paddingSize ( ) ) ) ;
}
// print tracks
const auto tracks = fileInfo . tracks ( ) ;
if ( ! tracks . empty ( ) ) {
cout < < " - " < < TextAttribute : : Bold < < " Tracks: " < < fileInfo . technicalSummary ( ) < < Phrases : : End ;
for ( const auto * track : tracks ) {
printProperty ( " ID " , track - > id ( ) , nullptr , true ) ;
printProperty ( " Name " , track - > name ( ) ) ;
printProperty ( " Type " , track - > mediaTypeName ( ) ) ;
if ( track - > language ( ) ! = " und " ) {
printProperty ( " Language " , track - > language ( ) ) ;
2015-08-09 23:53:45 +02:00
}
2017-09-22 00:19:24 +02:00
const char * fmtName = track - > formatName ( ) , * fmtAbbr = track - > formatAbbreviation ( ) ;
printProperty ( " Format " , fmtName ) ;
if ( strcmp ( fmtName , fmtAbbr ) ) {
printProperty ( " Abbreviation " , fmtAbbr ) ;
}
printProperty ( " Extensions " , track - > format ( ) . extensionName ( ) ) ;
printProperty ( " Raw format ID " , track - > formatId ( ) ) ;
if ( track - > size ( ) ) {
printProperty ( " Size " , dataSizeToString ( track - > size ( ) , true ) ) ;
}
printProperty ( " Duration " , track - > duration ( ) ) ;
printProperty ( " FPS " , track - > fps ( ) ) ;
if ( track - > channelConfigString ( ) ) {
printProperty ( " Channel config " , track - > channelConfigString ( ) ) ;
} else {
printProperty ( " Channel count " , track - > channelCount ( ) ) ;
}
if ( track - > extensionChannelConfigString ( ) ) {
printProperty ( " Extension channel config " , track - > extensionChannelConfigString ( ) ) ;
}
printProperty ( " Bitrate " , track - > bitrate ( ) , " kbit/s " ) ;
printProperty ( " Bits per sample " , track - > bitsPerSample ( ) ) ;
printProperty ( " Sampling frequency " , track - > samplingFrequency ( ) , " Hz " ) ;
printProperty ( " Extension sampling frequency " , track - > extensionSamplingFrequency ( ) , " Hz " ) ;
printProperty ( track - > mediaType ( ) = = MediaType : : Video
? " Frame count "
: " Sample count " ,
track - > sampleCount ( ) ) ;
printProperty ( " Creation time " , track - > creationTime ( ) ) ;
printProperty ( " Modification time " , track - > modificationTime ( ) ) ;
vector < string > labels ;
labels . reserve ( 7 ) ;
if ( track - > isInterlaced ( ) ) {
labels . emplace_back ( " interlaced " ) ;
}
if ( ! track - > isEnabled ( ) ) {
labels . emplace_back ( " disabled " ) ;
}
if ( track - > isDefault ( ) ) {
labels . emplace_back ( " default " ) ;
}
if ( track - > isForced ( ) ) {
labels . emplace_back ( " forced " ) ;
}
if ( track - > hasLacing ( ) ) {
labels . emplace_back ( " has lacing " ) ;
}
if ( track - > isEncrypted ( ) ) {
labels . emplace_back ( " encrypted " ) ;
}
if ( ! labels . empty ( ) ) {
printProperty ( " Labeled as " , joinStrings ( labels , " , " ) ) ;
}
cout < < ' \n ' ;
2015-08-09 23:53:45 +02:00
}
2017-09-22 00:19:24 +02:00
} else {
cout < < " - File has no (supported) tracks. \n " ;
2015-08-09 23:53:45 +02:00
}
2017-09-22 00:19:24 +02:00
// print attachments
const auto attachments = fileInfo . attachments ( ) ;
if ( ! attachments . empty ( ) ) {
cout < < " - " < < TextAttribute : : Bold < < " Attachments: " < < TextAttribute : : Reset < < ' \n ' ;
for ( const auto * attachment : attachments ) {
printProperty ( " ID " , attachment - > id ( ) ) ;
printProperty ( " Name " , attachment - > name ( ) ) ;
printProperty ( " MIME-type " , attachment - > mimeType ( ) ) ;
printProperty ( " Description " , attachment - > description ( ) ) ;
if ( attachment - > data ( ) ) {
printProperty ( " Size " , dataSizeToString ( static_cast < uint64 > ( attachment - > data ( ) - > size ( ) ) , true ) ) ;
2015-08-09 23:53:45 +02:00
}
2017-09-22 00:19:24 +02:00
cout < < ' \n ' ;
2015-08-09 23:53:45 +02:00
}
}
2017-09-22 00:19:24 +02:00
// print chapters
const auto chapters = fileInfo . chapters ( ) ;
if ( ! chapters . empty ( ) ) {
cout < < " - " < < TextAttribute : : Bold < < " Chapters: " < < TextAttribute : : Reset < < ' \n ' ;
for ( const auto * chapter : chapters ) {
printProperty ( " ID " , chapter - > id ( ) ) ;
if ( ! chapter - > names ( ) . empty ( ) ) {
printProperty ( " Name " , static_cast < string > ( chapter - > names ( ) . front ( ) ) ) ;
}
if ( ! chapter - > startTime ( ) . isNegative ( ) ) {
printProperty ( " Start time " , chapter - > startTime ( ) . toString ( ) ) ;
}
if ( ! chapter - > endTime ( ) . isNegative ( ) ) {
printProperty ( " End time " , chapter - > endTime ( ) . toString ( ) ) ;
2015-08-09 23:53:45 +02:00
}
2017-09-22 00:19:24 +02:00
cout < < ' \n ' ;
2015-08-09 23:53:45 +02:00
}
}
2017-09-22 00:19:24 +02:00
2017-10-09 19:01:49 +02:00
} catch ( const Media : : Failure & ) {
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " A parsing failure occured when reading the file \" " < < file < < " \" . " < < Phrases : : EndFlush ;
2016-06-14 22:54:49 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " An IO failure occured when reading the file \" " < < file < < " \" . " < < Phrases : : EndFlush ;
2015-08-09 23:53:45 +02:00
}
2017-09-22 00:19:24 +02:00
2015-09-23 00:02:06 +02:00
printNotifications ( fileInfo , " Parsing notifications: " , verboseArg . isPresent ( ) ) ;
2015-08-09 23:53:45 +02:00
cout < < endl ;
}
}
2016-06-25 23:11:16 +02:00
void displayTagInfo ( const Argument & fieldsArg , const Argument & filesArg , const Argument & verboseArg )
2015-04-22 19:33:53 +02:00
{
2015-09-01 20:20:15 +02:00
CMD_UTILS_START_CONSOLE ;
2017-10-09 20:34:08 +02:00
// check whether files have been specified
2016-06-25 23:11:16 +02:00
if ( ! filesArg . isPresent ( ) | | filesArg . values ( ) . empty ( ) ) {
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Error < < " No files have been specified. " < < Phrases : : End ;
2015-04-22 19:33:53 +02:00
return ;
}
2017-10-09 20:34:08 +02:00
// parse specified fields
2016-06-25 23:11:16 +02:00
const auto fields = parseFieldDenotations ( fieldsArg , true ) ;
2017-10-09 20:34:08 +02:00
2015-04-22 19:33:53 +02:00
MediaFileInfo fileInfo ;
2016-08-03 17:48:53 +02:00
for ( const char * file : filesArg . values ( ) ) {
2015-04-22 19:33:53 +02:00
try {
// parse tags
fileInfo . setPath ( file ) ;
fileInfo . open ( true ) ;
2016-08-03 17:48:53 +02:00
fileInfo . parseContainerFormat ( ) ;
2015-04-22 19:33:53 +02:00
fileInfo . parseTags ( ) ;
2017-09-22 00:19:24 +02:00
cout < < " Tag information for \" " < < file < < " \" : \n " ;
2015-08-09 23:53:45 +02:00
const auto tags = fileInfo . tags ( ) ;
2016-08-03 17:48:53 +02:00
if ( ! tags . empty ( ) ) {
2015-04-22 19:33:53 +02:00
// iterate through all tags
for ( const auto * tag : tags ) {
// determine tag type
2016-08-03 17:48:53 +02:00
const TagType tagType = tag - > type ( ) ;
2015-04-22 19:33:53 +02:00
// write tag name and target, eg. MP4/iTunes tag
2017-09-22 00:19:24 +02:00
cout < < " - " < < TextAttribute : : Bold < < tagName ( tag ) < < TextAttribute : : Reset < < ' \n ' ;
2015-04-22 19:33:53 +02:00
// iterate through fields specified by the user
2015-10-13 23:21:31 +02:00
if ( fields . empty ( ) ) {
for ( auto field = firstKnownField ; field ! = KnownField : : Invalid ; field = nextKnownField ( field ) ) {
2017-01-23 00:27:21 +01:00
printField ( FieldScope ( field ) , tag , tagType , true ) ;
2015-10-13 23:21:31 +02:00
}
} else {
2016-07-30 23:17:49 +02:00
for ( const auto & fieldDenotation : fields ) {
const FieldScope & denotedScope = fieldDenotation . first ;
if ( denotedScope . tagType = = TagType : : Unspecified | | ( denotedScope . tagType | tagType ) ! = TagType : : Unspecified ) {
2017-01-23 00:27:21 +01:00
printField ( denotedScope , tag , tagType , false ) ;
2015-04-22 19:33:53 +02:00
}
}
}
}
} else {
2017-09-22 00:19:24 +02:00
cout < < " - File has no (supported) tag information. \n " ;
2015-04-22 19:33:53 +02:00
}
2017-10-09 19:01:49 +02:00
} catch ( const Media : : Failure & ) {
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " A parsing failure occured when reading the file \" " < < file < < " \" . " < < Phrases : : EndFlush ;
2016-06-14 22:54:49 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " An IO failure occured when reading the file \" " < < file < < " \" . " < < Phrases : : EndFlush ;
2015-04-22 19:33:53 +02:00
}
2015-09-23 00:02:06 +02:00
printNotifications ( fileInfo , " Parsing notifications: " , verboseArg . isPresent ( ) ) ;
2015-08-09 23:53:45 +02:00
cout < < endl ;
2015-04-22 19:33:53 +02:00
}
}
2016-06-25 23:11:16 +02:00
void setTagInfo ( const SetTagInfoArgs & args )
2015-04-22 19:33:53 +02:00
{
2015-09-01 20:20:15 +02:00
CMD_UTILS_START_CONSOLE ;
2017-10-09 20:34:08 +02:00
// check whether files have been specified
2016-06-25 23:11:16 +02:00
if ( ! args . filesArg . isPresent ( ) | | args . filesArg . values ( ) . empty ( ) ) {
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " No files have been specified. " < < Phrases : : EndFlush ;
2015-04-22 19:33:53 +02:00
return ;
}
2016-08-07 22:03:52 +02:00
if ( args . outputFilesArg . isPresent ( ) & & args . outputFilesArg . values ( ) . size ( ) ! = args . filesArg . values ( ) . size ( ) ) {
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Error < < " The number of output files does not match the number of input files. " < < Phrases : : EndFlush ;
2016-08-07 22:03:52 +02:00
return ;
}
2017-10-09 20:34:08 +02:00
// get output files
2016-08-07 22:03:52 +02:00
auto & outputFiles = args . outputFilesArg . isPresent ( ) ? args . outputFilesArg . values ( ) : vector < const char * > ( ) ;
auto currentOutputFile = outputFiles . cbegin ( ) , noMoreOutputFiles = outputFiles . cend ( ) ;
2017-10-09 20:34:08 +02:00
// parse field denotations and check whether there's an operation to be done (changing fields or some other settings)
2016-06-25 23:11:16 +02:00
auto fields = parseFieldDenotations ( args . valuesArg , false ) ;
2016-07-31 23:22:22 +02:00
if ( fields . empty ( )
& & ( ! args . removeTargetArg . isPresent ( ) | | args . removeTargetArg . values ( ) . empty ( ) )
& & ( ! args . addAttachmentArg . isPresent ( ) | | args . addAttachmentArg . values ( ) . empty ( ) )
& & ( ! args . updateAttachmentArg . isPresent ( ) | | args . updateAttachmentArg . values ( ) . empty ( ) )
& & ( ! args . removeAttachmentArg . isPresent ( ) | | args . removeAttachmentArg . values ( ) . empty ( ) )
2016-08-05 01:48:36 +02:00
& & ( ! args . docTitleArg . isPresent ( ) | | args . docTitleArg . values ( ) . empty ( ) )
& & ! args . id3v1UsageArg . isPresent ( )
& & ! args . id3v2UsageArg . isPresent ( )
& & ! args . id3v2VersionArg . isPresent ( ) ) {
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Warning < < " No fields/attachments have been specified. " < < Phrases : : End ;
2015-04-22 19:33:53 +02:00
}
2017-10-09 20:34:08 +02:00
2015-10-13 23:21:31 +02:00
// determine required targets
vector < TagTarget > requiredTargets ;
2016-07-30 23:17:49 +02:00
for ( const auto & fieldDenotation : fields ) {
const FieldScope & scope = fieldDenotation . first ;
2017-06-14 00:09:10 +02:00
if ( ! scope . isTrack ( ) & & find ( requiredTargets . cbegin ( ) , requiredTargets . cend ( ) , scope . tagTarget ) = = requiredTargets . cend ( ) ) {
2016-07-30 23:17:49 +02:00
requiredTargets . push_back ( scope . tagTarget ) ;
2015-10-13 23:21:31 +02:00
}
}
2017-10-09 20:34:08 +02:00
2015-10-13 23:21:31 +02:00
// determine targets to remove
vector < TagTarget > targetsToRemove ;
bool validRemoveTargetsSpecified = false ;
2016-07-31 23:22:22 +02:00
for ( size_t i = 0 , max = args . removeTargetArg . occurrences ( ) ; i ! = max ; + + i ) {
for ( const auto & targetDenotation : args . removeTargetArg . values ( i ) ) {
targetsToRemove . emplace_back ( ) ;
2016-06-25 23:11:16 +02:00
if ( ! strcmp ( targetDenotation , " , " ) ) {
if ( validRemoveTargetsSpecified ) {
targetsToRemove . emplace_back ( ) ;
}
} else if ( applyTargetConfiguration ( targetsToRemove . back ( ) , targetDenotation ) ) {
validRemoveTargetsSpecified = true ;
} else {
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Warning < < " The given target specification \" " < < targetDenotation < < " \" is invalid and will be ignored. " < < Phrases : : End ;
2015-10-13 23:21:31 +02:00
}
}
}
2017-10-09 20:34:08 +02:00
// parse ID3v2 version
2015-04-22 19:33:53 +02:00
uint32 id3v2Version = 3 ;
2015-11-28 00:20:49 +01:00
if ( args . id3v2VersionArg . isPresent ( ) ) {
2015-04-22 19:33:53 +02:00
try {
2015-11-28 00:20:49 +01:00
id3v2Version = stringToNumber < uint32 > ( args . id3v2VersionArg . values ( ) . front ( ) ) ;
2015-04-22 19:33:53 +02:00
if ( id3v2Version < 1 | | id3v2Version > 4 ) {
throw ConversionException ( ) ;
}
2016-06-14 00:52:33 +02:00
} catch ( const ConversionException & ) {
2015-04-22 19:33:53 +02:00
id3v2Version = 3 ;
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Warning < < " The specified ID3v2 version \" " < < args . id3v2VersionArg . values ( ) . front ( ) < < " \" is invalid and will be ingored. " < < Phrases : : End ;
2015-04-22 19:33:53 +02:00
}
}
2017-10-09 20:34:08 +02:00
// parse other settings
2015-11-28 00:20:49 +01:00
const TagTextEncoding denotedEncoding = parseEncodingDenotation ( args . encodingArg , TagTextEncoding : : Utf8 ) ;
const TagUsage id3v1Usage = parseUsageDenotation ( args . id3v1UsageArg , TagUsage : : KeepExisting ) ;
const TagUsage id3v2Usage = parseUsageDenotation ( args . id3v2UsageArg , TagUsage : : Always ) ;
2017-10-09 20:34:08 +02:00
// setup media file info
2015-11-28 00:20:49 +01:00
MediaFileInfo fileInfo ;
fileInfo . setMinPadding ( parseUInt64 ( args . minPaddingArg , 0 ) ) ;
fileInfo . setMaxPadding ( parseUInt64 ( args . maxPaddingArg , 0 ) ) ;
fileInfo . setPreferredPadding ( parseUInt64 ( args . prefPaddingArg , 0 ) ) ;
2016-11-15 22:45:19 +01:00
fileInfo . setTagPosition ( parsePositionDenotation ( args . tagPosArg , args . tagPosValueArg , ElementPosition : : BeforeData ) ) ;
2015-11-28 00:20:49 +01:00
fileInfo . setForceTagPosition ( args . forceTagPosArg . isPresent ( ) ) ;
2016-11-15 22:45:19 +01:00
fileInfo . setIndexPosition ( parsePositionDenotation ( args . indexPosArg , args . indexPosValueArg , ElementPosition : : BeforeData ) ) ;
2015-11-28 00:20:49 +01:00
fileInfo . setForceIndexPosition ( args . forceIndexPosArg . isPresent ( ) ) ;
fileInfo . setForceRewrite ( args . forceRewriteArg . isPresent ( ) ) ;
2017-10-09 20:34:08 +02:00
2015-10-13 23:21:31 +02:00
// iterate through all specified files
2015-04-22 19:33:53 +02:00
unsigned int fileIndex = 0 ;
2015-10-06 22:41:02 +02:00
static const string context ( " setting tags " ) ;
NotificationList notifications ;
2016-08-03 17:48:53 +02:00
for ( const char * file : args . filesArg . values ( ) ) {
2015-04-22 19:33:53 +02:00
try {
2017-10-09 20:34:08 +02:00
// 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 ;
2015-10-06 22:41:02 +02:00
notifications . clear ( ) ;
2015-04-22 19:33:53 +02:00
fileInfo . setPath ( file ) ;
2016-08-03 17:48:53 +02:00
fileInfo . parseContainerFormat ( ) ;
2015-04-22 19:33:53 +02:00
fileInfo . parseTags ( ) ;
2015-10-06 22:41:02 +02:00
fileInfo . parseTracks ( ) ;
2015-10-13 23:21:31 +02:00
vector < Tag * > tags ;
2017-10-09 20:34:08 +02:00
2015-10-13 23:21:31 +02:00
// remove tags with the specified targets
if ( validRemoveTargetsSpecified ) {
fileInfo . tags ( tags ) ;
for ( auto * tag : tags ) {
if ( find ( targetsToRemove . cbegin ( ) , targetsToRemove . cend ( ) , tag - > target ( ) ) ! = targetsToRemove . cend ( ) ) {
fileInfo . removeTag ( tag ) ;
}
}
tags . clear ( ) ;
}
2017-10-09 20:34:08 +02:00
2015-10-13 23:21:31 +02:00
// create new tags according to settings
2016-08-05 01:48:36 +02:00
fileInfo . createAppropriateTags ( args . treatUnknownFilesAsMp3FilesArg . isPresent ( ) , id3v1Usage , id3v2Usage , args . id3InitOnCreateArg . isPresent ( ) , args . id3TransferOnRemovalArg . isPresent ( ) , args . mergeMultipleSuccessiveTagsArg . isPresent ( ) , ! args . id3v2VersionArg . isPresent ( ) , id3v2Version , requiredTargets ) ;
2015-10-06 22:41:02 +02:00
auto container = fileInfo . container ( ) ;
2016-06-25 23:11:16 +02:00
if ( args . docTitleArg . isPresent ( ) & & ! args . docTitleArg . values ( ) . empty ( ) ) {
2015-12-27 19:49:17 +01:00
if ( container & & container - > supportsTitle ( ) ) {
2015-10-14 19:49:48 +02:00
size_t segmentIndex = 0 , segmentCount = container - > titles ( ) . size ( ) ;
2015-11-28 00:20:49 +01:00
for ( const auto & newTitle : args . docTitleArg . values ( ) ) {
2015-10-14 19:49:48 +02:00
if ( segmentIndex < segmentCount ) {
container - > setTitle ( newTitle , segmentIndex ) ;
} else {
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Warning < < " The specified document title \" " < < newTitle < < " \" can not be set because the file has not that many segments. " < < Phrases : : End ;
2015-10-14 19:49:48 +02:00
}
+ + segmentIndex ;
}
} else {
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Warning < < " Setting the document title is not supported for the file. " < < Phrases : : End ;
2015-10-14 19:49:48 +02:00
}
}
2017-10-09 20:34:08 +02:00
2017-06-14 00:09:10 +02:00
// select the relevant values for the current file index
for ( auto & fieldDenotation : fields ) {
FieldValues & denotedValues = fieldDenotation . second ;
vector < FieldValue * > & relevantDenotedValues = denotedValues . relevantValues ;
denotedValues . relevantValues . clear ( ) ;
unsigned int currentFileIndex = 0 ;
for ( FieldValue & denotatedValue : denotedValues . allValues ) {
if ( denotatedValue . fileIndex < = fileIndex ) {
if ( relevantDenotedValues . empty ( ) | | ( denotatedValue . fileIndex > = currentFileIndex ) ) {
if ( currentFileIndex ! = denotatedValue . fileIndex ) {
currentFileIndex = denotatedValue . fileIndex ;
relevantDenotedValues . clear ( ) ;
}
relevantDenotedValues . push_back ( & denotatedValue ) ;
}
}
}
}
2017-10-09 20:34:08 +02:00
// alter tags
2015-10-13 23:21:31 +02:00
fileInfo . tags ( tags ) ;
2017-06-14 00:09:10 +02:00
if ( tags . empty ( ) ) {
fileInfo . addNotification ( NotificationType : : Critical , " Can not create appropriate tags for file. " , context ) ;
} else {
2015-04-22 19:33:53 +02:00
// iterate through all tags
for ( auto * tag : tags ) {
2016-07-30 23:17:49 +02:00
// clear current values if option is present
2015-11-28 00:20:49 +01:00
if ( args . removeOtherFieldsArg . isPresent ( ) ) {
2015-04-22 19:33:53 +02:00
tag - > removeAllFields ( ) ;
}
2016-07-30 23:17:49 +02:00
// determine required information for deciding whether specified values match the scope of the current tag
const auto tagType = tag - > type ( ) ;
const bool targetSupported = tag - > supportsTarget ( ) ;
const auto tagTarget = tag - > target ( ) ;
2017-05-18 02:32:51 +02:00
// determine the encoding to store text values
TagTextEncoding usedEncoding = denotedEncoding ;
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 ) ;
}
}
2016-07-30 23:17:49 +02:00
// iterate through all denoted field values
2017-06-14 00:09:10 +02:00
for ( const auto & fieldDenotation : fields ) {
2016-07-30 23:17:49 +02:00
const FieldScope & denotedScope = fieldDenotation . first ;
// decide whether the scope of the denotation matches of the current tag
2017-06-14 00:09:10 +02:00
if ( ! denotedScope . isTrack ( ) & & ( denotedScope . tagType = = TagType : : Unspecified
2016-07-30 23:17:49 +02:00
| | ( denotedScope . tagType & tagType ) ! = TagType : : Unspecified )
& & ( ! targetSupported | | denotedScope . tagTarget = = tagTarget ) ) {
2016-08-14 22:47:50 +02:00
// convert the values to TagValue
vector < TagValue > convertedValues ;
2017-06-14 00:09:10 +02:00
for ( const FieldValue * relevantDenotedValue : fieldDenotation . second . relevantValues ) {
2016-07-30 23:17:49 +02:00
// one of the denoted values
2016-08-14 22:47:50 +02:00
if ( ! relevantDenotedValue - > value . empty ( ) ) {
if ( relevantDenotedValue - > type = = DenotationType : : File ) {
2015-04-22 19:33:53 +02:00
try {
2016-07-30 23:17:49 +02:00
// assume the file refers to a picture
2016-08-14 22:47:50 +02:00
MediaFileInfo fileInfo ( relevantDenotedValue - > value ) ;
2015-04-22 19:33:53 +02:00
fileInfo . open ( true ) ;
fileInfo . parseContainerFormat ( ) ;
2015-08-16 23:41:49 +02:00
auto buff = make_unique < char [ ] > ( fileInfo . size ( ) ) ;
2015-04-22 19:33:53 +02:00
fileInfo . stream ( ) . seekg ( 0 ) ;
fileInfo . stream ( ) . read ( buff . get ( ) , fileInfo . size ( ) ) ;
TagValue value ( move ( buff ) , fileInfo . size ( ) , TagDataType : : Picture ) ;
value . setMimeType ( fileInfo . mimeType ( ) ) ;
2016-08-14 22:47:50 +02:00
convertedValues . emplace_back ( move ( value ) ) ;
2016-06-14 22:54:49 +02:00
} catch ( const Media : : Failure & ) {
2015-10-06 22:41:02 +02:00
fileInfo . addNotification ( NotificationType : : Critical , " Unable to parse specified cover file. " , context ) ;
2016-06-14 22:54:49 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
fileInfo . addNotification ( NotificationType : : Critical , " An IO error occured when parsing the specified cover file. " , context ) ;
2015-04-22 19:33:53 +02:00
}
2016-07-30 23:17:49 +02:00
} else {
2016-08-14 22:47:50 +02:00
convertedValues . emplace_back ( relevantDenotedValue - > value , TagTextEncoding : : Utf8 , usedEncoding ) ;
2015-04-22 19:33:53 +02:00
}
} else {
2016-07-30 23:17:49 +02:00
// if the denoted value is empty, just assign an empty TagValue to remove the field
2016-08-14 22:47:50 +02:00
convertedValues . emplace_back ( ) ;
2015-04-22 19:33:53 +02:00
}
}
2016-08-14 22:47:50 +02:00
// finally set the values
2017-01-23 00:27:21 +01:00
try {
denotedScope . field . setValues ( tag , tagType , convertedValues ) ;
} catch ( const ConversionException & e ) {
2017-05-18 02:32:51 +02:00
fileInfo . addNotification ( NotificationType : : Critical , argsToString ( " Unable to parse denoted field ID \" " , denotedScope . field . name ( ) , " \" : " , e . what ( ) ) , context ) ;
2017-01-23 00:27:21 +01:00
}
2015-04-22 19:33:53 +02:00
}
}
}
2017-06-14 00:09:10 +02:00
}
2017-10-09 20:34:08 +02:00
// alter tracks
2017-06-14 00:09:10 +02:00
for ( AbstractTrack * track : fileInfo . tracks ( ) ) {
for ( const auto & fieldDenotation : fields ) {
const auto & values = fieldDenotation . second . relevantValues ;
if ( values . empty ( ) ) {
continue ;
}
const FieldScope & denotedScope = fieldDenotation . first ;
// decide whether the scope of the denotation matches of the current track
if ( denotedScope . allTracks | | find ( denotedScope . trackIds . cbegin ( ) , denotedScope . trackIds . cend ( ) , track - > id ( ) ) ! = denotedScope . trackIds . cend ( ) ) {
const FieldId & field = denotedScope . field ;
const string & value = values . front ( ) - > value ;
try {
if ( field . denotes ( " name " ) ) {
track - > setName ( value ) ;
} else if ( field . denotes ( " language " ) ) {
track - > setLanguage ( value ) ;
} else if ( field . denotes ( " tracknumber " ) ) {
track - > setTrackNumber ( stringToNumber < uint32 > ( value ) ) ;
} else if ( field . denotes ( " enabled " ) ) {
track - > setEnabled ( stringToBool ( value ) ) ;
} else if ( field . denotes ( " forced " ) ) {
track - > setForced ( stringToBool ( value ) ) ;
} 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 ( ) ) ) ;
}
} 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 ( ) ) ) ;
}
}
}
}
2017-10-09 20:34:08 +02:00
2017-06-14 00:09:10 +02:00
// increment relevant values
for ( auto & fieldDenotation : fields ) {
for ( FieldValue * relevantDenotedValue : fieldDenotation . second . relevantValues ) {
if ( ! relevantDenotedValue - > value . empty ( ) & & relevantDenotedValue - > type = = DenotationType : : Increment ) {
relevantDenotedValue - > value = incremented ( relevantDenotedValue - > value ) ;
}
}
2015-10-06 22:41:02 +02:00
}
2017-10-09 20:34:08 +02:00
// alter attachments
2015-10-06 22:41:02 +02:00
bool attachmentsModified = false ;
2016-07-31 23:22:22 +02:00
if ( args . addAttachmentArg . isPresent ( ) | | args . updateAttachmentArg . isPresent ( ) | | args . removeAttachmentArg . isPresent ( ) | | args . removeExistingAttachmentsArg . isPresent ( ) ) {
2015-10-06 22:41:02 +02:00
static const string context ( " setting attachments " ) ;
fileInfo . parseAttachments ( ) ;
if ( fileInfo . attachmentsParsingStatus ( ) = = ParsingStatus : : Ok ) {
if ( container ) {
2015-10-13 23:21:31 +02:00
// ignore all existing attachments if argument is specified
2015-11-28 00:20:49 +01:00
if ( args . removeExistingAttachmentsArg . isPresent ( ) ) {
2015-10-13 23:21:31 +02:00
for ( size_t i = 0 , count = container - > attachmentCount ( ) ; i < count ; + + i ) {
container - > attachment ( i ) - > setIgnored ( false ) ;
}
attachmentsModified = true ;
}
2016-07-31 23:22:22 +02:00
// add/update/remove attachments
2015-10-06 22:41:02 +02:00
AttachmentInfo currentInfo ;
2016-07-31 23:22:22 +02:00
currentInfo . action = AttachmentAction : : Add ;
for ( size_t i = 0 , occurrences = args . addAttachmentArg . occurrences ( ) ; i ! = occurrences ; + + i ) {
for ( const char * value : args . addAttachmentArg . values ( i ) ) {
currentInfo . parseDenotation ( value ) ;
}
attachmentsModified | = currentInfo . next ( container ) ;
}
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 ) ;
}
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 ) ;
2015-10-06 22:41:02 +02:00
}
2016-07-31 23:22:22 +02:00
attachmentsModified | = currentInfo . next ( container ) ;
2015-10-06 22:41:02 +02:00
}
} else {
fileInfo . addNotification ( NotificationType : : Critical , " Unable to assign attachments because the container object has not been initialized. " , context ) ;
}
} else {
// notification will be added by the file info automatically
}
}
2017-10-09 20:34:08 +02:00
// apply changes
2016-11-19 21:45:52 +01:00
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 ( ) ;
2017-10-09 20:34:08 +02:00
// register handler for logging status
2017-09-22 00:19:24 +02:00
fileInfo . registerCallback ( logStatus ) ;
2017-10-09 20:34:08 +02:00
// register interrupt handler
const InterruptHandler handler ( [ & fileInfo ] {
fileInfo . tryToAbort ( ) ;
} ) ;
// apply changes and gather notifications
2016-11-19 21:45:52 +01:00
fileInfo . applyChanges ( ) ;
fileInfo . gatherRelatedNotifications ( notifications ) ;
2017-10-09 20:34:08 +02:00
// notify about completion
2017-09-22 00:19:24 +02:00
finalizeLog ( ) ;
cout < < " - Changes have been applied. " < < endl ;
2017-10-09 19:06:26 +02:00
} catch ( const Media : : OperationAbortedException & ) {
finalizeLog ( ) ;
2017-10-09 20:34:08 +02:00
cerr < < Phrases : : Warning < < " The operation has been aborted. " < < Phrases : : EndFlush ;
2017-10-09 19:06:26 +02:00
return ;
2017-10-09 19:01:49 +02:00
} catch ( const Media : : Failure & ) {
2017-09-22 00:19:24 +02:00
finalizeLog ( ) ;
2017-10-09 20:34:08 +02:00
cerr < < " - " < < Phrases : : Error < < " Failed to apply changes. " < < Phrases : : EndFlush ;
2015-04-22 19:33:53 +02:00
}
2017-10-09 19:01:49 +02:00
} catch ( const Media : : Failure & ) {
2017-09-22 00:19:24 +02:00
finalizeLog ( ) ;
2017-10-09 20:34:08 +02:00
cerr < < " - " < < Phrases : : Error < < " A parsing failure occured when reading/writing the file \" " < < file < < " \" . " < < Phrases : : EndFlush ;
2016-06-25 23:11:16 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
2017-09-22 00:19:24 +02:00
finalizeLog ( ) ;
2017-10-09 20:34:08 +02:00
cerr < < " - " < < Phrases : : Error < < " An IO failure occured when reading/writing the file \" " < < file < < " \" . " < < Phrases : : EndFlush ;
2015-04-22 19:33:53 +02:00
}
2017-10-09 20:34:08 +02:00
2015-11-28 00:20:49 +01:00
printNotifications ( notifications , " Notifications: " , args . verboseArg . isPresent ( ) ) ;
2016-08-07 22:03:52 +02:00
2017-10-09 20:34:08 +02:00
// continue with next file
2015-04-22 19:33:53 +02:00
+ + fileIndex ;
2016-08-07 22:03:52 +02:00
if ( currentOutputFile ! = noMoreOutputFiles ) {
+ + currentOutputFile ;
}
2015-04-22 19:33:53 +02:00
}
}
2016-08-03 17:48:53 +02:00
void extractField ( const Argument & fieldArg , const Argument & attachmentArg , const Argument & inputFilesArg , const Argument & outputFileArg , const Argument & verboseArg )
2015-04-22 19:33:53 +02:00
{
2015-09-01 20:20:15 +02:00
CMD_UTILS_START_CONSOLE ;
2016-08-03 17:48:53 +02:00
// parse specified field and attachment
const auto fieldDenotations = parseFieldDenotations ( fieldArg , true ) ;
AttachmentInfo attachmentInfo ;
if ( attachmentArg . isPresent ( ) ) {
attachmentInfo . parseDenotation ( attachmentArg . values ( ) . front ( ) ) ;
}
if ( ( ( fieldDenotations . size ( ) ! = 1 ) | | ( ! attachmentInfo . hasId & & ! attachmentInfo . name ) )
& & ( ( fieldDenotations . size ( ) = = 1 ) & & ( attachmentInfo . hasId | | attachmentInfo . name ) ) ) {
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Error < < " Excactly one field or attachment needs to be specified. " < < Phrases : : End ;
2015-04-22 19:33:53 +02:00
return ;
}
2016-08-03 17:48:53 +02:00
if ( ! inputFilesArg . isPresent ( ) | | inputFilesArg . values ( ) . empty ( ) ) {
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Error < < " No files have been specified. " < < Phrases : : End ;
2016-08-03 17:48:53 +02:00
return ;
}
2015-04-22 19:33:53 +02:00
MediaFileInfo inputFileInfo ;
2016-08-03 17:48:53 +02:00
for ( const char * file : inputFilesArg . values ( ) ) {
try {
2017-10-09 20:34:08 +02:00
// setup media file info
2016-08-03 17:48:53 +02:00
inputFileInfo . setPath ( file ) ;
inputFileInfo . open ( true ) ;
2017-10-09 20:34:08 +02:00
// extract either tag field or attachment
2016-08-03 17:48:53 +02:00
if ( ! fieldDenotations . empty ( ) ) {
// extract tag field
( outputFileArg . isPresent ( ) ? cout : cerr ) < < " Extracting field " < < fieldArg . values ( ) . front ( ) < < " of \" " < < file < < " \" ... " < < endl ;
inputFileInfo . parseContainerFormat ( ) ;
inputFileInfo . parseTags ( ) ;
auto tags = inputFileInfo . tags ( ) ;
vector < pair < const TagValue * , string > > values ;
// iterate through all tags
for ( const Tag * tag : tags ) {
2017-01-23 00:27:21 +01:00
const TagType tagType = tag - > type ( ) ;
for ( const pair < FieldScope , FieldValues > & fieldDenotation : fieldDenotations ) {
try {
for ( const TagValue * value : fieldDenotation . first . field . values ( tag , tagType ) ) {
values . emplace_back ( value , joinStrings ( { tag - > typeName ( ) , numberToString ( values . size ( ) ) } , " - " , true ) ) ;
}
} catch ( const ConversionException & e ) {
2017-01-30 22:13:49 +01:00
inputFileInfo . addNotification ( NotificationType : : Critical , " Unable to parse denoted field ID \" " % string ( fieldDenotation . first . field . name ( ) ) % " \" : " + e . what ( ) , " extracting field " ) ;
2016-08-03 17:48:53 +02:00
}
}
2015-04-22 19:33:53 +02:00
}
2016-08-03 17:48:53 +02:00
if ( values . empty ( ) ) {
2017-09-22 00:19:24 +02:00
cerr < < " - " < < Phrases : : Error < < " None of the specified files has a (supported) " < < fieldArg . values ( ) . front ( ) < < " field. " < < Phrases : : End ;
2016-08-03 17:48:53 +02:00
} else if ( outputFileArg . isPresent ( ) ) {
string outputFilePathWithoutExtension , outputFileExtension ;
if ( values . size ( ) > 1 ) {
outputFilePathWithoutExtension = BasicFileInfo : : pathWithoutExtension ( outputFileArg . values ( ) . front ( ) ) ;
outputFileExtension = BasicFileInfo : : extension ( outputFileArg . values ( ) . front ( ) ) ;
}
for ( const auto & value : values ) {
2016-12-19 23:53:12 +01:00
NativeFileStream outputFileStream ;
2016-08-03 17:48:53 +02:00
outputFileStream . exceptions ( ios_base : : failbit | ios_base : : badbit ) ;
auto path = values . size ( ) > 1 ? joinStrings ( { outputFilePathWithoutExtension , " - " , value . second , outputFileExtension } ) : outputFileArg . values ( ) . front ( ) ;
try {
outputFileStream . open ( path , ios_base : : out | ios_base : : binary ) ;
outputFileStream . write ( value . first - > dataPointer ( ) , value . first - > dataSize ( ) ) ;
outputFileStream . flush ( ) ;
2017-09-22 00:19:24 +02:00
cout < < " - Value has been saved to \" " < < path < < " \" . " < < endl ;
2016-08-03 17:48:53 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
2017-09-22 00:19:24 +02:00
cerr < < " - " < < Phrases : : Error < < " An IO error occured when writing the file \" " < < path < < " \" . " < < Phrases : : End ;
2016-08-03 17:48:53 +02:00
}
}
} else {
// write data to stdout if no output file has been specified
for ( const auto & value : values ) {
cout . write ( value . first - > dataPointer ( ) , value . first - > dataSize ( ) ) ;
}
}
} else {
// extract attachment
auto & logStream = ( outputFileArg . isPresent ( ) ? cout : cerr ) ;
logStream < < " Extracting attachment with " ;
if ( attachmentInfo . hasId ) {
logStream < < " ID " < < attachmentInfo . id ;
} else {
logStream < < " name \" " < < attachmentInfo . name < < ' \" ' ;
}
logStream < < " of \" " < < file < < " \" ... " < < endl ;
inputFileInfo . parseContainerFormat ( ) ;
inputFileInfo . parseAttachments ( ) ;
vector < pair < const AbstractAttachment * , string > > attachments ;
// iterate through all attachments
for ( const AbstractAttachment * attachment : inputFileInfo . attachments ( ) ) {
2016-08-05 01:48:36 +02:00
if ( ( attachmentInfo . hasId & & attachment - > id ( ) = = attachmentInfo . id ) | | ( attachment - > name ( ) = = attachmentInfo . name ) ) {
2016-08-03 17:48:53 +02:00
attachments . emplace_back ( attachment , joinStrings ( { attachment - > name ( ) , numberToString ( attachments . size ( ) ) } , " - " , true ) ) ;
}
}
if ( attachments . empty ( ) ) {
2017-09-22 00:19:24 +02:00
cerr < < " - " < < Phrases : : Error < < " None of the specified files has a (supported) attachment with the specified ID/name. " < < Phrases : : End ;
2016-08-03 17:48:53 +02:00
} else if ( outputFileArg . isPresent ( ) ) {
string outputFilePathWithoutExtension , outputFileExtension ;
if ( attachments . size ( ) > 1 ) {
outputFilePathWithoutExtension = BasicFileInfo : : pathWithoutExtension ( outputFileArg . values ( ) . front ( ) ) ;
outputFileExtension = BasicFileInfo : : extension ( outputFileArg . values ( ) . front ( ) ) ;
}
for ( const auto & attachment : attachments ) {
2016-12-19 23:53:12 +01:00
NativeFileStream outputFileStream ;
2016-08-03 17:48:53 +02:00
outputFileStream . exceptions ( ios_base : : failbit | ios_base : : badbit ) ;
auto path = attachments . size ( ) > 1 ? joinStrings ( { outputFilePathWithoutExtension , " - " , attachment . second , outputFileExtension } ) : outputFileArg . values ( ) . front ( ) ;
try {
outputFileStream . open ( path , ios_base : : out | ios_base : : binary ) ;
attachment . first - > data ( ) - > copyTo ( outputFileStream ) ;
outputFileStream . flush ( ) ;
2017-09-22 00:19:24 +02:00
cout < < " - Value has been saved to \" " < < path < < " \" . " < < endl ;
2016-08-03 17:48:53 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
2017-10-09 20:34:08 +02:00
cerr < < " - " < < Phrases : : Error < < " An IO error occured when writing the file \" " < < path < < " \" . " < < Phrases : : EndFlush ;
2016-08-03 17:48:53 +02:00
}
}
} else {
for ( const auto & attachment : attachments ) {
attachment . first - > data ( ) - > copyTo ( cout ) ;
}
2015-04-22 19:33:53 +02:00
}
}
2016-08-03 17:48:53 +02:00
2017-10-09 19:01:49 +02:00
} catch ( const Media : : Failure & ) {
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Error < < " A parsing failure occured when reading the file \" " < < file < < " \" . " < < Phrases : : End ;
2016-08-03 17:48:53 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
2017-09-22 00:19:24 +02:00
cerr < < Phrases : : Error < < " An IO failure occured when reading the file \" " < < file < < " \" . " < < Phrases : : End ;
2015-04-22 19:33:53 +02:00
}
2016-08-03 17:48:53 +02:00
printNotifications ( inputFileInfo , " Parsing notifications: " , verboseArg . isPresent ( ) ) ;
2015-04-22 19:33:53 +02:00
}
}
2018-01-26 18:09:53 +01:00
void exportToJson ( const ArgumentOccurrence & , const Argument & filesArg , const Argument & prettyArg )
2018-01-15 00:14:53 +01:00
{
CMD_UTILS_START_CONSOLE ;
# ifdef TAGEDITOR_JSON_EXPORT
// check whether files have been specified
if ( ! filesArg . isPresent ( ) | | filesArg . values ( ) . empty ( ) ) {
cerr < < Phrases : : Error < < " No files have been specified. " < < Phrases : : End ;
return ;
}
RAPIDJSON_NAMESPACE : : Document document ( RAPIDJSON_NAMESPACE : : kArrayType ) ;
std : : vector < Json : : FileInfo > jsonData ;
MediaFileInfo fileInfo ;
// gather tags for each file
for ( const char * file : filesArg . values ( ) ) {
try {
// parse tags
fileInfo . setPath ( file ) ;
fileInfo . open ( true ) ;
fileInfo . parseContainerFormat ( ) ;
fileInfo . parseTags ( ) ;
fileInfo . parseTracks ( ) ;
jsonData . emplace_back ( fileInfo , document . GetAllocator ( ) ) ;
} catch ( const Media : : Failure & ) {
cerr < < Phrases : : Error < < " A parsing failure occured when reading the file \" " < < file < < " \" . " < < Phrases : : EndFlush ;
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
cerr < < Phrases : : Error < < " An IO failure occured when reading the file \" " < < file < < " \" . " < < Phrases : : EndFlush ;
}
}
// print the gathered data as JSON document
ReflectiveRapidJSON : : JsonReflector : : push ( jsonData , document , document . GetAllocator ( ) ) ;
RAPIDJSON_NAMESPACE : : OStreamWrapper osw ( cout ) ;
2018-01-26 18:09:53 +01:00
if ( prettyArg . isPresent ( ) ) {
RAPIDJSON_NAMESPACE : : PrettyWriter < RAPIDJSON_NAMESPACE : : OStreamWrapper > writer ( osw ) ;
document . Accept ( writer ) ;
} else {
RAPIDJSON_NAMESPACE : : Writer < RAPIDJSON_NAMESPACE : : OStreamWrapper > writer ( osw ) ;
document . Accept ( writer ) ;
}
2018-01-15 00:14:53 +01:00
cout < < endl ;
# else
VAR_UNUSED ( filesArg ) ;
cerr < < Phrases : : Error < < " JSON export has not been enabled when building the tag editor. " < < Phrases : : EndFlush ;
# endif
}
2017-11-29 22:57:32 +01:00
void applyGeneralConfig ( const Argument & timeSapnFormatArg )
{
timeSpanOutputFormat = parseTimeSpanOutputFormat ( timeSapnFormatArg , TimeSpanOutputFormat : : WithMeasures ) ;
}
2015-04-22 19:33:53 +02:00
}