2015-09-06 20:20:00 +02:00
# include "./mainfeatures.h"
2015-09-06 15:41:17 +02:00
2015-09-06 20:20:00 +02:00
# include "../application/knownfieldmodel.h"
2016-07-27 18:31:42 +02:00
# if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
# 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>
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
# 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>
# 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>
2015-04-22 19:33:53 +02:00
# include <c++utilities/misc/memory.h>
2016-07-27 18:31:42 +02:00
# if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
# include <QDir>
# endif
2015-04-22 19:33:53 +02:00
# include <iostream>
2016-07-27 18:31:42 +02:00
# include <cstring>
# include <algorithm>
2016-07-30 23:17:49 +02:00
# include <unordered_map>
2015-04-22 19:33:53 +02:00
using namespace std ;
using namespace ApplicationUtilities ;
using namespace ConversionUtilities ;
using namespace ChronoUtilities ;
using namespace EscapeCodes ;
using namespace Settings ;
using namespace Media ;
2016-07-27 18:31:42 +02:00
# if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
using namespace Utility ;
# endif
2015-04-22 19:33:53 +02:00
namespace Cli {
2016-07-30 23:17:49 +02:00
// define enums, operators and structs to handle specified field denotations
2015-04-22 19:33:53 +02:00
enum class DenotationType
{
Normal ,
Increment ,
File
} ;
inline TagType operator | ( TagType lhs , TagType rhs )
{
return static_cast < TagType > ( static_cast < unsigned int > ( lhs ) | static_cast < unsigned int > ( rhs ) ) ;
}
2016-07-30 23:17:49 +02:00
inline TagType operator & ( TagType lhs , TagType rhs )
{
return static_cast < TagType > ( static_cast < unsigned int > ( lhs ) & static_cast < unsigned int > ( rhs ) ) ;
}
2015-04-22 19:33:53 +02:00
inline TagType & operator | = ( TagType & lhs , TagType rhs )
{
return lhs = static_cast < TagType > ( static_cast < unsigned int > ( lhs ) | static_cast < unsigned int > ( rhs ) ) ;
}
2016-07-30 23:17:49 +02:00
struct FieldScope
2015-04-22 19:33:53 +02:00
{
2016-07-30 23:17:49 +02:00
FieldScope ( KnownField field = KnownField : : Invalid , TagType tagType = TagType : : Unspecified , TagTarget tagTarget = TagTarget ( ) ) ;
bool operator = = ( const FieldScope & other ) const ;
2015-10-13 23:21:31 +02:00
KnownField field ;
2015-04-22 19:33:53 +02:00
TagType tagType ;
TagTarget tagTarget ;
} ;
2016-07-30 23:17:49 +02:00
FieldScope : : FieldScope ( KnownField field , TagType tagType , TagTarget tagTarget ) :
2015-10-13 23:21:31 +02:00
field ( field ) ,
2016-07-30 23:17:49 +02:00
tagType ( tagType ) ,
tagTarget ( tagTarget )
2015-04-22 19:33:53 +02:00
{ }
2016-07-30 23:17:49 +02:00
bool FieldScope : : operator = = ( const FieldScope & other ) const
{
return field = = other . field & & tagType = = other . tagType & & tagTarget = = other . tagTarget ;
}
struct FieldValue
{
FieldValue ( DenotationType type , unsigned int fileIndex , const char * value ) ;
DenotationType type ;
unsigned int fileIndex ;
string value ;
} ;
inline FieldValue : : FieldValue ( DenotationType type , unsigned int fileIndex , const char * value ) :
type ( type ) ,
fileIndex ( fileIndex ) ,
value ( value )
{ }
}
namespace std {
using namespace Cli ;
template < > struct hash < TagTarget : : IdContainerType >
{
std : : size_t operator ( ) ( const TagTarget : : IdContainerType & ids ) const
{
using std : : hash ;
auto seed = ids . size ( ) ;
for ( auto id : ids ) {
seed ^ = id + 0x9e3779b9 + ( seed < < 6 ) + ( seed > > 2 ) ;
}
return seed ;
}
} ;
template < > struct hash < TagTarget >
{
std : : size_t operator ( ) ( const TagTarget & target ) const
{
using std : : hash ;
return ( ( hash < uint64 > ( ) ( target . level ( ) )
^ ( hash < TagTarget : : IdContainerType > ( ) ( target . tracks ( ) ) < < 1 ) ) > > 1 )
^ ( hash < TagTarget : : IdContainerType > ( ) ( target . attachments ( ) ) < < 1 ) ;
}
} ;
template < > struct hash < FieldScope >
{
std : : size_t operator ( ) ( const FieldScope & scope ) const
{
using std : : hash ;
return ( ( hash < KnownField > ( ) ( scope . field )
^ ( hash < TagType > ( ) ( scope . tagType ) < < 1 ) ) > > 1 )
^ ( hash < TagTarget > ( ) ( scope . tagTarget ) < < 1 ) ;
}
} ;
}
namespace Cli {
typedef vector < FieldValue > FieldValues ;
typedef unordered_map < FieldScope , FieldValues > FieldDenotations ;
constexpr bool isDigit ( char c )
2015-04-22 19:33:53 +02:00
{
return c > = ' 0 ' & & c < = ' 9 ' ;
}
2016-07-27 18:31:42 +02:00
string incremented ( const string & str , unsigned int toIncrement = 1 )
2015-04-22 19:33:53 +02:00
{
2016-07-27 18:31:42 +02:00
string res ;
2015-04-22 19:33:53 +02:00
res . reserve ( str . size ( ) ) ;
unsigned int value = 0 ;
bool hasValue = false ;
2016-07-27 18:31:42 +02:00
for ( const char & c : str ) {
if ( toIncrement & & c > = ' 0 ' & & c < = ' 9 ' ) {
value = value * 10 + static_cast < unsigned int > ( c - ' 0 ' ) ;
2015-04-22 19:33:53 +02:00
hasValue = true ;
} else {
if ( hasValue ) {
2016-07-27 18:31:42 +02:00
res + = numberToString ( value + 1 ) ;
2015-04-22 19:33:53 +02:00
hasValue = false ;
- - toIncrement ;
}
2016-07-27 18:31:42 +02:00
res + = c ;
2015-04-22 19:33:53 +02:00
}
}
2016-07-30 23:17:49 +02:00
if ( hasValue ) {
res + = numberToString ( value + 1 ) ;
}
2015-04-22 19:33:53 +02:00
return res ;
}
2015-10-06 22:41:02 +02:00
void printNotifications ( NotificationList & notifications , const char * head = nullptr , bool beVerbose = false )
2015-04-22 19:33:53 +02:00
{
2015-09-23 00:02:06 +02:00
if ( ! beVerbose ) {
for ( const auto & notification : notifications ) {
switch ( notification . type ( ) ) {
case NotificationType : : Debug :
case NotificationType : : Information :
break ;
default :
goto printNotifications ;
}
}
return ;
}
2015-04-22 19:33:53 +02:00
if ( ! notifications . empty ( ) ) {
2016-07-30 23:17:49 +02:00
printNotifications :
2015-04-22 19:33:53 +02:00
if ( head ) {
cout < < head < < endl ;
}
Notification : : sortByTime ( notifications ) ;
2015-09-23 00:02:06 +02:00
for ( const auto & notification : notifications ) {
2015-04-22 19:33:53 +02:00
switch ( notification . type ( ) ) {
case NotificationType : : Debug :
2015-09-23 00:02:06 +02:00
if ( beVerbose ) {
cout < < " Debug " ;
2015-10-06 22:41:02 +02:00
break ;
} else {
continue ;
2015-09-23 00:02:06 +02:00
}
case NotificationType : : Information :
if ( beVerbose ) {
cout < < " Information " ;
2015-10-06 22:41:02 +02:00
break ;
} else {
continue ;
2015-09-23 00:02:06 +02:00
}
2015-04-22 19:33:53 +02:00
case NotificationType : : Warning :
cout < < " Warning " ;
break ;
2015-09-23 00:02:06 +02:00
case NotificationType : : Critical :
cout < < " Error " ;
break ;
2015-04-22 19:33:53 +02:00
default :
2015-09-23 00:02:06 +02:00
;
2015-04-22 19:33:53 +02:00
}
cout < < notification . creationTime ( ) . toString ( DateTimeOutputFormat : : TimeOnly ) < < " " ;
cout < < notification . context ( ) < < " : " ;
cout < < notification . message ( ) < < endl ;
}
}
}
2015-10-06 22:41:02 +02:00
void printNotifications ( const MediaFileInfo & fileInfo , const char * head = nullptr , bool beVerbose = false )
{
NotificationList notifications ;
fileInfo . gatherRelatedNotifications ( notifications ) ;
printNotifications ( notifications , head , beVerbose ) ;
}
2016-07-31 23:22:22 +02:00
# define FIELD_NAMES "title album artist genre year comment bpm bps lyricist track disk part totalparts encoder\n" \
" recorddate performers duration language encodersettings lyrics synchronizedlyrics grouping \n " \
" recordlabel cover composer rating description "
2016-07-04 23:28:11 +02:00
2016-07-31 23:22:22 +02:00
# define TAG_MODIFIER "tag=id3v1 tag=id3v2 tag=id3 tag=itunes tag=vorbis tag=matroska tag=all"
# define TARGET_MODIFIER "target-level target-levelname target-tracks target-tracks\n" \
" target-chapters target-editions target-attachments target-reset "
const char * const fieldNames = FIELD_NAMES ;
const char * const fieldNamesForSet = FIELD_NAMES " " TAG_MODIFIER " " TARGET_MODIFIER ;
void printFieldNames ( const ArgumentOccurrence & occurrence )
2015-04-22 19:33:53 +02:00
{
2015-09-01 20:20:15 +02:00
CMD_UTILS_START_CONSOLE ;
2016-07-31 23:22:22 +02:00
VAR_UNUSED ( occurrence )
cout < < fieldNames ;
cout < < " \n Tag modifier: " < < TAG_MODIFIER ;
cout < < " \n Target modifier: " < < TARGET_MODIFIER < < endl ;
2015-04-22 19:33:53 +02:00
}
TagUsage parseUsageDenotation ( const Argument & usageArg , TagUsage defaultUsage )
{
if ( usageArg . isPresent ( ) ) {
const auto & val = usageArg . values ( ) . front ( ) ;
2016-06-14 00:52:33 +02:00
if ( ! strcmp ( val , " never " ) ) {
2015-04-22 19:33:53 +02:00
return TagUsage : : Never ;
2016-06-14 00:52:33 +02:00
} else if ( ! strcmp ( val , " keepexisting " ) ) {
2015-04-22 19:33:53 +02:00
return TagUsage : : KeepExisting ;
2016-06-14 00:52:33 +02:00
} else if ( ! strcmp ( val , " always " ) ) {
2015-04-22 19:33:53 +02:00
return TagUsage : : Always ;
} else {
2016-06-14 22:54:49 +02:00
cerr < < " Warning: The specified tag usage \" " < < val < < " \" is invalid and will be ignored. " < < endl ;
2015-04-22 19:33:53 +02:00
}
}
return defaultUsage ;
}
TagTextEncoding parseEncodingDenotation ( const Argument & encodingArg , TagTextEncoding defaultEncoding )
{
if ( encodingArg . isPresent ( ) ) {
const auto & val = encodingArg . values ( ) . front ( ) ;
2016-06-14 00:52:33 +02:00
if ( ! strcmp ( val , " utf8 " ) ) {
2015-04-22 19:33:53 +02:00
return TagTextEncoding : : Utf8 ;
2016-06-14 00:52:33 +02:00
} else if ( ! strcmp ( val , " latin1 " ) ) {
2015-04-22 19:33:53 +02:00
return TagTextEncoding : : Latin1 ;
2016-06-14 00:52:33 +02:00
} else if ( ! strcmp ( val , " utf16be " ) ) {
2015-04-22 19:33:53 +02:00
return TagTextEncoding : : Utf16BigEndian ;
2016-06-14 00:52:33 +02:00
} else if ( ! strcmp ( val , " utf16le " ) ) {
2015-04-22 19:33:53 +02:00
return TagTextEncoding : : Utf16LittleEndian ;
2016-06-14 00:52:33 +02:00
} else if ( ! strcmp ( val , " auto " ) ) {
2015-04-22 19:33:53 +02:00
} else {
2016-06-14 22:54:49 +02:00
cerr < < " Warning: The specified encoding \" " < < val < < " \" is invalid and will be ignored. " < < endl ;
2015-04-22 19:33:53 +02:00
}
}
return defaultEncoding ;
}
2015-11-28 00:20:49 +01:00
ElementPosition parsePositionDenotation ( const Argument & posArg , ElementPosition defaultPos )
{
if ( posArg . isPresent ( ) ) {
const auto & val = posArg . values ( ) . front ( ) ;
2016-06-14 00:52:33 +02:00
if ( ! strcmp ( val , " front " ) ) {
2015-11-28 00:20:49 +01:00
return ElementPosition : : BeforeData ;
2016-06-14 00:52:33 +02:00
} else if ( ! strcmp ( val , " back " ) ) {
2015-11-28 00:20:49 +01:00
return ElementPosition : : AfterData ;
2016-06-14 00:52:33 +02:00
} else if ( ! strcmp ( val , " keep " ) ) {
2015-11-28 00:20:49 +01:00
return ElementPosition : : Keep ;
} else {
2016-06-14 22:54:49 +02:00
cerr < < " Warning: The specified position \" " < < val < < " \" is invalid and will be ignored. " < < endl ;
2015-11-28 00:20:49 +01:00
}
}
return defaultPos ;
}
uint64 parseUInt64 ( const Argument & arg , uint64 defaultValue )
{
if ( arg . isPresent ( ) ) {
try {
2016-06-14 00:52:33 +02:00
if ( * arg . values ( ) . front ( ) = = ' 0 ' & & * ( arg . values ( ) . front ( ) + 1 ) = = ' x ' ) {
return stringToNumber < decltype ( parseUInt64 ( arg , defaultValue ) ) > ( arg . values ( ) . front ( ) + 2 , 16 ) ;
2015-11-28 00:20:49 +01:00
} else {
return stringToNumber < decltype ( parseUInt64 ( arg , defaultValue ) ) > ( arg . values ( ) . front ( ) ) ;
}
} catch ( const ConversionException & ) {
2016-06-14 22:54:49 +02:00
cerr < < " Warning: The specified value \" " < < arg . values ( ) . front ( ) < < " \" is no valid unsigned integer and will be ignored. " < < endl ;
2015-11-28 00:20:49 +01:00
}
}
return defaultValue ;
}
2015-04-22 19:33:53 +02:00
TagTarget : : IdContainerType parseIds ( const std : : string & concatenatedIds )
{
auto splittedIds = splitString ( concatenatedIds , " , " , EmptyPartsTreat : : Omit ) ;
TagTarget : : IdContainerType convertedIds ;
convertedIds . reserve ( splittedIds . size ( ) ) ;
for ( const auto & id : splittedIds ) {
try {
convertedIds . push_back ( stringToNumber < TagTarget : : IdType > ( id ) ) ;
2015-11-28 00:20:49 +01:00
} catch ( const ConversionException & ) {
2016-06-14 22:54:49 +02:00
cerr < < " Warning: The specified ID \" " < < id < < " \" is invalid and will be ignored. " < < endl ;
2015-04-22 19:33:53 +02:00
}
}
return convertedIds ;
}
bool applyTargetConfiguration ( TagTarget & target , const std : : string & configStr )
{
2015-10-13 23:21:31 +02:00
if ( ! configStr . empty ( ) ) {
if ( configStr . compare ( 0 , 13 , " target-level= " ) = = 0 ) {
try {
target . setLevel ( stringToNumber < uint64 > ( configStr . substr ( 13 ) ) ) ;
2015-11-28 00:20:49 +01:00
} catch ( const ConversionException & ) {
2016-06-14 22:54:49 +02:00
cerr < < " Warning: The specified target level \" " < < configStr . substr ( 13 ) < < " \" is invalid and will be ignored. " < < endl ;
2015-10-13 23:21:31 +02:00
}
} else if ( configStr . compare ( 0 , 17 , " target-levelname= " ) = = 0 ) {
target . setLevelName ( configStr . substr ( 17 ) ) ;
} else if ( configStr . compare ( 0 , 14 , " target-tracks= " ) = = 0 ) {
target . tracks ( ) = parseIds ( configStr . substr ( 14 ) ) ;
} else if ( configStr . compare ( 0 , 16 , " target-chapters= " ) = = 0 ) {
target . chapters ( ) = parseIds ( configStr . substr ( 16 ) ) ;
} else if ( configStr . compare ( 0 , 16 , " target-editions= " ) = = 0 ) {
target . editions ( ) = parseIds ( configStr . substr ( 16 ) ) ;
2016-07-31 23:22:22 +02:00
} else if ( configStr . compare ( 0 , 19 , " target-attachments= " ) = = 0 ) {
2015-10-13 23:21:31 +02:00
target . attachments ( ) = parseIds ( configStr . substr ( 17 ) ) ;
2016-07-31 23:22:22 +02:00
} else if ( configStr . compare ( 0 , 13 , " target-reset= " ) = = 0 ) {
if ( * ( configStr . data ( ) + 13 ) ) {
cerr < < " Warning: Invalid assignment " < < ( configStr . data ( ) + 13 ) < < " for target-reset will be ignored. " < < endl ;
}
target . clear ( ) ;
2015-10-13 23:21:31 +02:00
} else if ( configStr = = " target-reset " ) {
target . clear ( ) ;
} else {
return false ;
}
return true ;
2015-04-22 19:33:53 +02:00
} else {
return false ;
}
}
2016-07-30 23:17:49 +02:00
FieldDenotations parseFieldDenotations ( const Argument & fieldsArg , bool readOnly )
2015-04-22 19:33:53 +02:00
{
2016-07-30 23:17:49 +02:00
FieldDenotations fields ;
2016-06-25 23:11:16 +02:00
if ( fieldsArg . isPresent ( ) ) {
const vector < const char * > & fieldDenotations = fieldsArg . values ( ) ;
2016-07-30 23:17:49 +02:00
FieldScope scope ;
2016-06-25 23:11:16 +02:00
for ( const char * fieldDenotationString : fieldDenotations ) {
// check for tag or target specifier
const auto fieldDenotationLen = strlen ( fieldDenotationString ) ;
2016-07-30 23:17:49 +02:00
if ( ! strncmp ( fieldDenotationString , " tag= " , 4 ) ) {
2016-06-25 23:11:16 +02:00
if ( fieldDenotationLen = = 4 ) {
cerr < < " Warning: The \" tag \" -specifier has been used with no value(s) and hence is ignored. Possible values are: id3,id3v1,id3v2,itunes,vorbis,matroska,all " < < endl ;
} else {
TagType tagType = TagType : : Unspecified ;
for ( const auto & part : splitString ( fieldDenotationString + 4 , " , " , EmptyPartsTreat : : Omit ) ) {
if ( part = = " id3v1 " ) {
tagType | = TagType : : Id3v1Tag ;
} else if ( part = = " id3v2 " ) {
tagType | = TagType : : Id3v2Tag ;
} else if ( part = = " id3 " ) {
tagType | = TagType : : Id3v1Tag | TagType : : Id3v2Tag ;
} else if ( part = = " itunes " | | part = = " mp4 " ) {
tagType | = TagType : : Mp4Tag ;
} else if ( part = = " vorbis " ) {
tagType | = TagType : : VorbisComment ;
} else if ( part = = " matroska " ) {
tagType | = TagType : : MatroskaTag ;
} else if ( part = = " all " | | part = = " any " ) {
tagType = TagType : : Unspecified ;
break ;
} else {
cerr < < " Warning: The value provided with the \" tag \" -specifier is invalid and will be ignored. Possible values are: id3,id3v1,id3v2,itunes,vorbis,matroska,all " < < endl ;
2016-07-30 23:17:49 +02:00
tagType = scope . tagType ;
2016-06-25 23:11:16 +02:00
break ;
}
2015-04-22 19:33:53 +02:00
}
2016-07-30 23:17:49 +02:00
scope . tagType = tagType ;
2016-06-25 23:11:16 +02:00
break ;
2015-04-22 19:33:53 +02:00
}
2016-07-30 23:17:49 +02:00
} else if ( applyTargetConfiguration ( scope . tagTarget , fieldDenotationString ) ) {
2016-06-25 23:11:16 +02:00
continue ;
2015-04-22 19:33:53 +02:00
}
2016-06-25 23:11:16 +02:00
// read field name
const auto equationPos = strchr ( fieldDenotationString , ' = ' ) ;
size_t fieldNameLen = equationPos ? static_cast < size_t > ( equationPos - fieldDenotationString ) : strlen ( fieldDenotationString ) ;
// field name might denote increment ("+") or path disclosure (">")
DenotationType type = DenotationType : : Normal ;
if ( fieldNameLen & & equationPos ) {
switch ( * ( equationPos - 1 ) ) {
case ' + ' :
type = DenotationType : : Increment ;
- - fieldNameLen ;
break ;
case ' > ' :
type = DenotationType : : File ;
- - fieldNameLen ;
break ;
default :
;
}
2015-04-22 19:33:53 +02:00
}
2016-06-25 23:11:16 +02:00
// field name might specify a file index
unsigned int fileIndex = 0 , mult = 1 ;
for ( const char * digitPos = fieldDenotationString + fieldNameLen - 1 ; fieldNameLen & & isDigit ( * digitPos ) ; - - fieldNameLen , - - digitPos , mult * = 10 ) {
fileIndex + = static_cast < unsigned int > ( * digitPos - ' 0 ' ) * mult ;
}
if ( ! fieldNameLen ) {
cerr < < " Warning: Ignoring field denotation \" " < < fieldDenotationString < < " \" because no field name has been specified. " < < endl ;
continue ;
}
// parse the denoted filed
if ( ! strncmp ( fieldDenotationString , " title " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Title ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " album " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Album ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " artist " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Artist ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " genre " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Genre ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " year " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Year ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " comment " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Comment ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " bpm " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Bpm ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " bps " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Bps ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " lyricist " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Lyricist ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " track " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : TrackPosition ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " disk " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : DiskPosition ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " part " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : PartNumber ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " totalparts " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : TotalParts ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " encoder " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Encoder ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " recorddate " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : RecordDate ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " performers " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Performers ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " duration " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Length ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " language " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Language ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " encodersettings " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : EncoderSettings ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " lyrics " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Lyrics ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " synchronizedlyrics " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : SynchronizedLyrics ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " grouping " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Grouping ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " recordlabel " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : RecordLabel ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " cover " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Cover ;
2016-06-25 23:11:16 +02:00
type = DenotationType : : File ; // read cover always from file
} else if ( ! strncmp ( fieldDenotationString , " composer " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Composer ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " rating " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Rating ;
2016-06-25 23:11:16 +02:00
} else if ( ! strncmp ( fieldDenotationString , " description " , fieldNameLen ) ) {
2016-07-30 23:17:49 +02:00
scope . field = KnownField : : Description ;
2015-04-22 19:33:53 +02:00
} else {
2016-06-25 23:11:16 +02:00
// no "KnownField" value matching -> discard the field denotation
cerr < < " Warning: The field name \" " < < string ( fieldDenotationString , fieldNameLen ) < < " \" is unknown and will be ingored. " < < endl ;
continue ;
}
2016-07-30 23:17:49 +02:00
// add field denotation scope
auto & fieldValues = fields [ scope ] ;
// add value to the scope (if present)
2016-06-25 23:11:16 +02:00
if ( equationPos ) {
if ( readOnly ) {
cerr < < " Warning: Specified value for \" " < < string ( fieldDenotationString , fieldNameLen ) < < " \" will be ignored. " < < endl ;
} else {
2016-07-30 23:17:49 +02:00
// file index might have been specified explicitely
// if not (mult == 1) use the index of the last value and increase it by one
// if there are no previous values, just use the index 0
fieldValues . emplace_back ( FieldValue ( type , mult = = 1 ? ( fieldValues . empty ( ) ? 0 : fieldValues . back ( ) . fileIndex + 1 ) : fileIndex , ( equationPos + 1 ) ) ) ;
2016-06-25 23:11:16 +02:00
}
2015-04-22 19:33:53 +02:00
}
}
}
2015-10-13 23:21:31 +02:00
return fields ;
2015-04-22 19:33:53 +02:00
}
2015-10-06 22:41:02 +02:00
enum class AttachmentAction {
Add ,
2016-07-31 23:22:22 +02:00
Update ,
Remove
2015-10-06 22:41:02 +02:00
} ;
class AttachmentInfo
{
public :
AttachmentInfo ( ) ;
2016-07-31 23:22:22 +02:00
void parseDenotation ( const char * denotation ) ;
2015-10-06 22:41:02 +02:00
void apply ( AbstractContainer * container ) ;
void apply ( AbstractAttachment * attachment ) ;
void reset ( ) ;
bool next ( AbstractContainer * container ) ;
AttachmentAction action ;
uint64 id ;
2016-07-31 23:22:22 +02:00
bool hasId ;
const char * path ;
const char * name ;
const char * mime ;
const char * desc ;
2015-10-06 22:41:02 +02:00
} ;
AttachmentInfo : : AttachmentInfo ( ) :
action ( AttachmentAction : : Add ) ,
2016-07-31 23:22:22 +02:00
id ( 0 ) ,
hasId ( false ) ,
path ( nullptr ) ,
name ( nullptr ) ,
mime ( nullptr ) ,
desc ( nullptr )
2015-10-06 22:41:02 +02:00
{ }
2016-07-31 23:22:22 +02:00
void AttachmentInfo : : parseDenotation ( const char * denotation )
{
if ( ! strncmp ( denotation , " id= " , 3 ) ) {
try {
id = stringToNumber < uint64 , string > ( denotation + 3 ) ;
hasId = true ;
} catch ( const ConversionException & ) {
cerr < < " The specified attachment ID \" " < < ( denotation + 3 ) < < " \" is invalid. " ;
}
} else if ( ! strncmp ( denotation , " path= " , 5 ) ) {
path = denotation + 5 ;
} else if ( ! strncmp ( denotation , " name= " , 5 ) ) {
name = denotation + 5 ;
} else if ( ! strncmp ( denotation , " mime= " , 5 ) ) {
mime = denotation + 5 ;
} else if ( ! strncmp ( denotation , " desc= " , 5 ) ) {
desc = denotation + 5 ;
} else {
cerr < < " The attachment specification \" " < < denotation < < " \" is invalid and will be ignored. " ;
}
}
2015-10-06 22:41:02 +02:00
void AttachmentInfo : : apply ( AbstractContainer * container )
{
static const string context ( " applying specified attachments " ) ;
AbstractAttachment * attachment = nullptr ;
bool attachmentFound = false ;
switch ( action ) {
case AttachmentAction : : Add :
2016-07-31 23:22:22 +02:00
if ( ! path | | ! name ) {
cerr < < " Argument --update-argument specified but no name/path provided. " < < endl ;
2015-10-06 22:41:02 +02:00
return ;
}
apply ( container - > createAttachment ( ) ) ;
break ;
2016-07-31 23:22:22 +02:00
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 ) ;
attachmentFound = true ;
}
2015-10-06 22:41:02 +02:00
}
2016-07-31 23:22:22 +02:00
if ( ! attachmentFound ) {
container - > addNotification ( NotificationType : : Critical , " Attachment with the specified ID \" " + numberToString ( id ) + " \" does not exist and hence can't be updated. " , context ) ;
2015-10-06 22:41:02 +02:00
}
2016-07-31 23:22:22 +02:00
} else if ( name ) {
for ( size_t i = 0 , count = container - > attachmentCount ( ) ; i < count ; + + i ) {
attachment = container - > attachment ( i ) ;
if ( attachment - > name ( ) = = name ) {
apply ( attachment ) ;
attachmentFound = true ;
}
2015-10-06 22:41:02 +02:00
}
2016-07-31 23:22:22 +02:00
if ( ! attachmentFound ) {
container - > addNotification ( NotificationType : : Critical , " Attachment with the specified name \" " + string ( name ) + " \" does not exist and hence can't be updated. " , context ) ;
}
} else {
cerr < < " Argument --update-argument specified but no ID/name provided. " < < endl ;
2015-10-06 22:41:02 +02:00
}
break ;
2016-07-31 23:22:22 +02:00
case AttachmentAction : : Remove :
if ( hasId ) {
for ( size_t i = 0 , count = container - > attachmentCount ( ) ; i < count ; + + i ) {
attachment = container - > attachment ( i ) ;
if ( attachment - > id ( ) = = id ) {
attachment - > setIgnored ( true ) ;
attachmentFound = true ;
}
2015-10-06 22:41:02 +02:00
}
2016-07-31 23:22:22 +02:00
if ( ! attachmentFound ) {
container - > addNotification ( NotificationType : : 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 ) {
attachment = container - > attachment ( i ) ;
if ( attachment - > name ( ) = = name ) {
attachment - > setIgnored ( true ) ;
attachmentFound = true ;
}
}
if ( ! attachmentFound ) {
container - > addNotification ( NotificationType : : 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 ;
2015-10-06 22:41:02 +02:00
}
break ;
}
}
void AttachmentInfo : : apply ( AbstractAttachment * attachment )
{
2016-07-31 23:22:22 +02:00
if ( hasId ) {
2015-10-06 22:41:02 +02:00
attachment - > setId ( id ) ;
}
2016-07-31 23:22:22 +02:00
if ( path ) {
2015-10-06 22:41:02 +02:00
attachment - > setFile ( path ) ;
}
2016-07-31 23:22:22 +02:00
if ( name ) {
2015-10-06 22:41:02 +02:00
attachment - > setName ( name ) ;
}
2016-07-31 23:22:22 +02:00
if ( mime ) {
2015-10-06 22:41:02 +02:00
attachment - > setMimeType ( mime ) ;
}
2016-07-31 23:22:22 +02:00
if ( desc ) {
2015-10-06 22:41:02 +02:00
attachment - > setDescription ( desc ) ;
}
}
void AttachmentInfo : : reset ( )
{
action = AttachmentAction : : Add ;
id = 0 ;
2016-07-31 23:22:22 +02:00
hasId = false ;
path = name = mime = desc = nullptr ;
2015-10-06 22:41:02 +02:00
}
bool AttachmentInfo : : next ( AbstractContainer * container )
{
2016-07-31 23:22:22 +02:00
if ( ! id & & ! path & & ! name & & ! mime & & ! desc ) {
2015-10-06 22:41:02 +02:00
// skip empty attachment infos
return false ;
}
apply ( container ) ;
reset ( ) ;
return true ;
}
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 ;
2016-07-27 18:31:42 +02:00
# if defined(GUI_QTWIDGETS) || defined(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 ( ) ;
cout < < " Saving file info of \" " < < inputFileArg . values ( ) . front ( ) < < " \" ... " < < endl ;
NotificationList origNotify ;
2016-06-14 00:52:33 +02:00
QFile file ( QString : : fromLocal8Bit ( outputFileArg . values ( ) . front ( ) ) ) ;
2015-04-22 19:33: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 {
2016-06-14 22:54:49 +02:00
cerr < < " Error: An IO error occured when writing the file \" " < < outputFileArg . values ( ) . front ( ) < < " \" . " < < endl ;
2015-04-22 19:33:53 +02:00
}
2016-06-14 22:54:49 +02:00
} catch ( const ApplicationUtilities : : Failure & ) {
cerr < < " Error: A parsing failure occured when reading the file \" " < < inputFileArg . values ( ) . front ( ) < < " \" . " < < endl ;
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
cerr < < " Error: An IO failure occured when reading the file \" " < < inputFileArg . values ( ) . front ( ) < < " \" . " < < endl ;
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 ) ;
cerr < < " Error: Generating HTML info is only available if built with Qt support. " < < endl ;
# endif
2015-04-22 19:33:53 +02:00
}
2015-08-09 23:53:45 +02:00
void printProperty ( const char * propName , const char * value , const char * suffix = nullptr , size_t intention = 4 )
{
if ( * value ) {
for ( ; intention ; - - intention ) {
cout < < ' ' ;
}
cout < < propName ;
2015-09-24 01:15:48 +02:00
for ( intention = strlen ( propName ) ; intention < 30 ; + + intention ) {
2015-08-09 23:53:45 +02:00
cout < < ' ' ;
}
cout < < value ;
if ( suffix ) {
cout < < ' ' < < suffix ;
}
cout < < endl ;
}
}
void printProperty ( const char * propName , const string & value , const char * suffix = nullptr , size_t intention = 4 )
{
printProperty ( propName , value . data ( ) , suffix , intention ) ;
}
2015-10-14 19:49:48 +02:00
void printProperty ( const char * propName , TimeSpan timeSpan , const char * suffix = nullptr , size_t intention = 4 )
{
if ( ! timeSpan . isNull ( ) ) {
printProperty ( propName , timeSpan . toString ( TimeSpanOutputFormat : : WithMeasures ) , suffix , intention ) ;
}
}
void printProperty ( const char * propName , DateTime dateTime , const char * suffix = nullptr , size_t intention = 4 )
{
if ( ! dateTime . isNull ( ) ) {
printProperty ( propName , dateTime . toString ( ) , suffix , intention ) ;
}
}
2015-08-09 23:53:45 +02:00
template < typename intType >
void printProperty ( const char * propName , const intType value , const char * suffix = nullptr , bool force = false , size_t intention = 4 )
{
if ( value ! = 0 | | force ) {
printProperty ( propName , numberToString < intType > ( value ) , suffix , intention ) ;
}
}
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 ;
2016-06-25 23:11:16 +02:00
if ( ! filesArg . isPresent ( ) | | filesArg . values ( ) . empty ( ) ) {
2016-07-30 23:17:49 +02:00
cerr < < " Error: No files have been specified. " < < endl ;
2015-08-09 23:53:45 +02:00
return ;
}
MediaFileInfo fileInfo ;
for ( const auto & file : filesArg . values ( ) ) {
try {
// parse tags
fileInfo . setPath ( file ) ;
fileInfo . open ( true ) ;
fileInfo . parseTracks ( ) ;
fileInfo . parseAttachments ( ) ;
fileInfo . parseChapters ( ) ;
cout < < " Technical information for \" " < < file < < " \" : " < < endl ;
cout < < " Container format: " < < fileInfo . containerFormatName ( ) < < endl ;
2015-10-14 19:49:48 +02:00
{
if ( const auto container = fileInfo . container ( ) ) {
size_t segmentIndex = 0 ;
for ( const auto & title : container - > titles ( ) ) {
if ( segmentIndex ) {
printProperty ( " Title " , title + " (segment " + numberToString ( + + segmentIndex ) + " ) " ) ;
} else {
+ + segmentIndex ;
printProperty ( " Title " , title ) ;
}
}
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 ( ) ) ;
}
if ( fileInfo . paddingSize ( ) ) {
printProperty ( " Padding " , dataSizeToString ( fileInfo . paddingSize ( ) ) ) ;
}
}
2015-08-09 23:53:45 +02:00
{ // tracks
const auto tracks = fileInfo . tracks ( ) ;
if ( ! tracks . empty ( ) ) {
cout < < " Tracks: " < < endl ;
for ( const auto * track : tracks ) {
printProperty ( " ID " , track - > id ( ) , nullptr , true ) ;
printProperty ( " Name " , track - > name ( ) ) ;
printProperty ( " Type " , track - > mediaTypeName ( ) ) ;
2015-09-24 01:15:48 +02:00
const char * fmtName = track - > formatName ( ) , * fmtAbbr = track - > formatAbbreviation ( ) ;
printProperty ( " Format " , fmtName ) ;
if ( strcmp ( fmtName , fmtAbbr ) ) {
printProperty ( " Abbreviation " , fmtAbbr ) ;
}
2015-09-24 00:19:53 +02:00
printProperty ( " Extensions " , track - > format ( ) . extensionName ( ) ) ;
2015-08-09 23:53:45 +02:00
printProperty ( " Raw format ID " , track - > formatId ( ) ) ;
if ( track - > size ( ) ) {
printProperty ( " Size " , dataSizeToString ( track - > size ( ) , true ) ) ;
}
2015-10-14 19:49:48 +02:00
printProperty ( " Duration " , track - > duration ( ) ) ;
2015-08-09 23:53:45 +02:00
printProperty ( " FPS " , track - > fps ( ) ) ;
if ( track - > channelConfigString ( ) ) {
printProperty ( " Channel config " , track - > channelConfigString ( ) ) ;
} else {
printProperty ( " Channel count " , track - > channelCount ( ) ) ;
}
2015-09-24 01:15:48 +02:00
if ( track - > extensionChannelConfigString ( ) ) {
printProperty ( " Extension channel config " , track - > extensionChannelConfigString ( ) ) ;
}
2015-08-09 23:53:45 +02:00
printProperty ( " Bitrate " , track - > bitrate ( ) , " kbit/s " ) ;
printProperty ( " Bits per sample " , track - > bitsPerSample ( ) ) ;
2015-09-24 01:15:48 +02:00
printProperty ( " Sampling frequency " , track - > samplingFrequency ( ) , " Hz " ) ;
2015-08-09 23:53:45 +02:00
printProperty ( " Extension sampling frequency " , track - > extensionSamplingFrequency ( ) , " Hz " ) ;
printProperty ( " Sample count " , track - > sampleCount ( ) ) ;
2015-10-14 19:49:48 +02:00
printProperty ( " Creation time " , track - > creationTime ( ) ) ;
printProperty ( " Modification time " , track - > modificationTime ( ) ) ;
2015-08-09 23:53:45 +02:00
cout < < endl ;
}
} else {
cout < < " File has no (supported) tracks. " < < endl ;
}
}
{ // attachments
const auto attachments = fileInfo . attachments ( ) ;
if ( ! attachments . empty ( ) ) {
2016-07-31 23:22:22 +02:00
cout < < " Attachments: " < < endl ;
2015-08-09 23:53:45 +02:00
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 ( attachment - > data ( ) - > size ( ) , true ) ) ;
}
cout < < endl ;
}
}
}
{ // chapters
const auto chapters = fileInfo . chapters ( ) ;
if ( ! chapters . empty ( ) ) {
2016-07-31 23:22:22 +02:00
cout < < " Chapters: " < < endl ;
2015-10-06 22:41:02 +02:00
for ( const auto * chapter : chapters ) {
2015-08-09 23:53:45 +02:00
printProperty ( " ID " , chapter - > id ( ) ) ;
if ( ! chapter - > names ( ) . empty ( ) ) {
printProperty ( " Name " , static_cast < string > ( chapter - > names ( ) . front ( ) ) ) ;
}
2016-07-11 21:12:02 +02:00
if ( ! chapter - > startTime ( ) . isNegative ( ) ) {
2015-08-09 23:53:45 +02:00
printProperty ( " Start time " , chapter - > startTime ( ) . toString ( ) ) ;
}
2016-07-11 21:12:02 +02:00
if ( ! chapter - > endTime ( ) . isNegative ( ) ) {
2015-08-09 23:53:45 +02:00
printProperty ( " End time " , chapter - > endTime ( ) . toString ( ) ) ;
}
cout < < endl ;
}
}
}
2016-06-14 22:54:49 +02:00
} catch ( const ApplicationUtilities : : Failure & ) {
cerr < < " Error: A parsing failure occured when reading the file \" " < < file < < " \" . " < < endl ;
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
cerr < < " Error: An IO failure occured when reading the file \" " < < file < < " \" . " < < endl ;
2015-08-09 23:53:45 +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 ;
2016-06-25 23:11:16 +02:00
if ( ! filesArg . isPresent ( ) | | filesArg . values ( ) . empty ( ) ) {
2016-07-30 23:17:49 +02:00
cerr < < " Error: No files have been specified. " < < endl ;
2015-04-22 19:33:53 +02:00
return ;
}
2016-06-25 23:11:16 +02:00
const auto fields = parseFieldDenotations ( fieldsArg , true ) ;
2015-04-22 19:33:53 +02:00
MediaFileInfo fileInfo ;
for ( const auto & file : filesArg . values ( ) ) {
try {
// parse tags
fileInfo . setPath ( file ) ;
fileInfo . open ( true ) ;
fileInfo . parseTags ( ) ;
cout < < " Tag information for \" " < < file < < " \" : " < < endl ;
2015-08-09 23:53:45 +02:00
const auto tags = fileInfo . tags ( ) ;
2015-04-22 19:33:53 +02:00
if ( tags . size ( ) ) {
// iterate through all tags
for ( const auto * tag : tags ) {
// determine tag type
TagType tagType = tag - > type ( ) ;
// write tag name and target, eg. MP4/iTunes tag
cout < < tag - > typeName ( ) ;
2016-07-30 23:17:49 +02:00
if ( tagType = = TagType : : MatroskaTag | | ! tag - > target ( ) . isEmpty ( ) ) {
2016-05-26 02:15:41 +02:00
cout < < " targeting \" " < < tag - > targetString ( ) < < " \" " ;
2015-04-22 19:33:53 +02:00
}
cout < < endl ;
// 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 ) ) {
const auto & value = tag - > value ( field ) ;
if ( ! value . isEmpty ( ) ) {
// write field name
const char * fieldName = KnownFieldModel : : fieldName ( field ) ;
cout < < ' ' < < fieldName ;
// write padding
for ( auto i = strlen ( fieldName ) ; i < 18 ; + + i ) {
cout < < ' ' ;
}
// write value
2015-04-22 19:33:53 +02:00
try {
2016-07-27 18:31:42 +02:00
const auto textValue = value . toString ( TagTextEncoding : : Utf8 ) ;
if ( textValue . empty ( ) ) {
2015-04-22 19:33:53 +02:00
cout < < " can't display here (see --extract) " ;
} else {
2016-07-27 18:31:42 +02:00
cout < < textValue ;
2015-04-22 19:33:53 +02:00
}
2016-06-14 22:54:49 +02:00
} catch ( const ConversionException & ) {
2015-04-22 19:33:53 +02:00
cout < < " conversion error " ;
}
2015-10-13 23:21:31 +02:00
cout < < endl ;
}
}
} else {
2016-07-30 23:17:49 +02:00
for ( const auto & fieldDenotation : fields ) {
const FieldScope & denotedScope = fieldDenotation . first ;
const TagValue & value = tag - > value ( denotedScope . field ) ;
if ( denotedScope . tagType = = TagType : : Unspecified | | ( denotedScope . tagType | tagType ) ! = TagType : : Unspecified ) {
2015-10-13 23:21:31 +02:00
// write field name
2016-07-30 23:17:49 +02:00
const char * fieldName = KnownFieldModel : : fieldName ( denotedScope . field ) ;
2015-10-13 23:21:31 +02:00
cout < < ' ' < < fieldName ;
// write padding
for ( auto i = strlen ( fieldName ) ; i < 18 ; + + i ) {
cout < < ' ' ;
}
// write value
if ( value . isEmpty ( ) ) {
cout < < " none " ;
} else {
try {
2016-07-27 18:31:42 +02:00
const auto textValue = value . toString ( TagTextEncoding : : Utf8 ) ;
if ( textValue . empty ( ) ) {
2015-10-13 23:21:31 +02:00
cout < < " can't display here (see --extract) " ;
} else {
2016-07-27 18:31:42 +02:00
cout < < textValue ;
2015-10-13 23:21:31 +02:00
}
2016-06-14 22:54:49 +02:00
} catch ( const ConversionException & ) {
2015-10-13 23:21:31 +02:00
cout < < " conversion error " ;
}
}
cout < < endl ;
2015-04-22 19:33:53 +02:00
}
}
}
}
} else {
cout < < " File has no (supported) tag information. " < < endl ;
}
2016-06-14 22:54:49 +02:00
} catch ( const ApplicationUtilities : : Failure & ) {
cerr < < " Error: A parsing failure occured when reading the file \" " < < file < < " \" . " < < endl ;
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
cerr < < " Error: An IO failure occured when reading the file \" " < < file < < " \" . " < < endl ;
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 ;
2016-06-25 23:11:16 +02:00
if ( ! args . filesArg . isPresent ( ) | | args . filesArg . values ( ) . empty ( ) ) {
2016-06-14 22:54:49 +02:00
cerr < < " Error: No files have been specified. " < < endl ;
2015-04-22 19:33:53 +02:00
return ;
}
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 ( ) )
& & ( ! args . docTitleArg . isPresent ( ) | | args . docTitleArg . values ( ) . empty ( ) ) ) {
2016-06-14 22:54:49 +02:00
cerr < < " Error: No fields/attachments have been specified. " < < endl ;
2015-04-22 19:33:53 +02:00
return ;
}
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 ;
if ( find ( requiredTargets . cbegin ( ) , requiredTargets . cend ( ) , scope . tagTarget ) = = requiredTargets . cend ( ) ) {
requiredTargets . push_back ( scope . tagTarget ) ;
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 {
cerr < < " Warning: The given target specification \" " < < targetDenotation < < " \" is invalid and will be ignored. " < < endl ;
2015-10-13 23:21:31 +02:00
}
}
}
// parse other settings
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 ;
2016-06-14 22:54:49 +02:00
cerr < < " Warning: The specified ID3v2 version \" " < < args . id3v2VersionArg . values ( ) . front ( ) < < " \" is invalid and will be ingored. " < < endl ;
2015-04-22 19:33:53 +02:00
}
}
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 ) ;
MediaFileInfo fileInfo ;
fileInfo . setMinPadding ( parseUInt64 ( args . minPaddingArg , 0 ) ) ;
fileInfo . setMaxPadding ( parseUInt64 ( args . maxPaddingArg , 0 ) ) ;
fileInfo . setPreferredPadding ( parseUInt64 ( args . prefPaddingArg , 0 ) ) ;
fileInfo . setTagPosition ( parsePositionDenotation ( args . tagPosArg , ElementPosition : : BeforeData ) ) ;
fileInfo . setForceTagPosition ( args . forceTagPosArg . isPresent ( ) ) ;
fileInfo . setIndexPosition ( parsePositionDenotation ( args . indexPosArg , ElementPosition : : BeforeData ) ) ;
fileInfo . setForceIndexPosition ( args . forceIndexPosArg . isPresent ( ) ) ;
fileInfo . setForceRewrite ( args . forceRewriteArg . isPresent ( ) ) ;
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 ;
2015-11-28 00:20:49 +01:00
for ( const auto & file : args . filesArg . values ( ) ) {
2015-04-22 19:33:53 +02:00
try {
// parse tags
2015-10-06 22:41:02 +02:00
cout < < " Setting tag information for \" " < < file < < " \" ... " < < endl ;
notifications . clear ( ) ;
2015-04-22 19:33:53 +02:00
fileInfo . setPath ( file ) ;
fileInfo . parseTags ( ) ;
2015-10-06 22:41:02 +02:00
fileInfo . parseTracks ( ) ;
2015-10-13 23:21:31 +02:00
vector < Tag * > tags ;
// 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 ( ) ;
}
// create new tags according to settings
2015-11-28 00:20:49 +01:00
fileInfo . createAppropriateTags ( args . treatUnknownFilesAsMp3FilesArg . isPresent ( ) , id3v1Usage , id3v2Usage , args . mergeMultipleSuccessiveTagsArg . isPresent ( ) , ! args . id3v2VersionArg . isPresent ( ) , id3v2Version , requiredTargets ) ;
2015-10-06 22:41:02 +02:00
auto container = fileInfo . container ( ) ;
2015-10-14 19:49:48 +02:00
bool docTitleModified = false ;
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 ) ;
docTitleModified = true ;
} else {
2016-06-14 22:54:49 +02:00
cerr < < " Warning: The specified document title \" " < < newTitle < < " \" can not be set because the file has not that many segments. " < < endl ;
2015-10-14 19:49:48 +02:00
}
+ + segmentIndex ;
}
} else {
2016-06-14 22:54:49 +02:00
cerr < < " Warning: Setting the document title is not supported for the file. " < < endl ;
2015-10-14 19:49:48 +02:00
}
}
2015-10-13 23:21:31 +02:00
fileInfo . tags ( tags ) ;
2015-10-06 22:41:02 +02:00
if ( ! tags . empty ( ) ) {
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 ( ) ;
// iterate through all denoted field values
for ( auto & fieldDenotation : fields ) {
const FieldScope & denotedScope = fieldDenotation . first ;
FieldValues & denotedValues = fieldDenotation . second ;
// decide whether the scope of the denotation matches of the current tag
if ( ( denotedScope . tagType = = TagType : : Unspecified
| | ( denotedScope . tagType & tagType ) ! = TagType : : Unspecified )
& & ( ! targetSupported | | denotedScope . tagTarget = = tagTarget ) ) {
// select the value for the current file index
FieldValue * selectedDenotedValue = nullptr ;
for ( FieldValue & denotatedValue : denotedValues ) {
if ( denotatedValue . fileIndex < = fileIndex ) {
if ( ! selectedDenotedValue | | ( denotatedValue . fileIndex > selectedDenotedValue - > fileIndex ) ) {
selectedDenotedValue = & denotatedValue ;
2015-04-22 19:33:53 +02:00
}
}
}
2016-07-30 23:17:49 +02:00
if ( selectedDenotedValue ) {
// one of the denoted values
if ( ! selectedDenotedValue - > value . empty ( ) ) {
if ( selectedDenotedValue - > 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
MediaFileInfo fileInfo ( selectedDenotedValue - > 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-07-30 23:17:49 +02:00
tag - > setValue ( denotedScope . field , 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 {
TagTextEncoding usedEncoding = denotedEncoding ;
if ( ! tag - > canEncodingBeUsed ( denotedEncoding ) ) {
usedEncoding = tag - > proposedTextEncoding ( ) ;
}
tag - > setValue ( denotedScope . field , TagValue ( selectedDenotedValue - > value , TagTextEncoding : : Utf8 , usedEncoding ) ) ;
if ( selectedDenotedValue - > type = = DenotationType : : Increment & & tag = = tags . back ( ) ) {
selectedDenotedValue - > value = incremented ( selectedDenotedValue - > value ) ;
}
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
tag - > setValue ( denotedScope . field , TagValue ( ) ) ;
2015-04-22 19:33:53 +02:00
}
}
}
}
}
2015-10-06 22:41:02 +02:00
} else {
fileInfo . addNotification ( NotificationType : : Critical , " Can not create appropriate tags for file. " , context ) ;
}
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
}
}
2015-10-14 19:49:48 +02:00
if ( ! tags . empty ( ) | | docTitleModified | | attachmentsModified ) {
2015-04-22 19:33:53 +02:00
try {
2015-10-06 22:41:02 +02:00
// save parsing notifications because notifications of sub objects like tags, tracks, ... will be gone after applying changes
fileInfo . gatherRelatedNotifications ( notifications ) ;
fileInfo . invalidateNotifications ( ) ;
2015-04-22 19:33:53 +02:00
fileInfo . applyChanges ( ) ;
2015-10-06 22:41:02 +02:00
fileInfo . gatherRelatedNotifications ( notifications ) ;
2015-04-22 19:33:53 +02:00
cout < < " Changes have been applied. " < < endl ;
2015-10-06 22:41:02 +02:00
} catch ( const ApplicationUtilities : : Failure & ) {
2016-06-14 22:54:49 +02:00
cerr < < " Error: Failed to apply changes. " < < endl ;
2015-04-22 19:33:53 +02:00
}
2015-10-14 19:49:48 +02:00
} else {
2016-06-14 22:54:49 +02:00
cerr < < " Warning: No changed to be applied. " < < endl ;
2015-04-22 19:33:53 +02:00
}
2015-10-06 22:41:02 +02:00
} catch ( const ApplicationUtilities : : Failure & ) {
2016-06-14 22:54:49 +02:00
cerr < < " Error: A parsing failure occured when reading/writing the file \" " < < file < < " \" . " < < endl ;
2016-06-25 23:11:16 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
cerr < < " Error: An IO failure occured when reading/writing the file \" " < < file < < " \" . " < < endl ;
2015-04-22 19:33:53 +02:00
}
2015-11-28 00:20:49 +01:00
printNotifications ( notifications , " Notifications: " , args . verboseArg . isPresent ( ) ) ;
2015-04-22 19:33:53 +02:00
+ + fileIndex ;
}
}
2016-06-25 23:11:16 +02:00
void extractField ( const Argument & fieldsArg , const Argument & inputFileArg , 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-06-25 23:11:16 +02:00
const auto fields = parseFieldDenotations ( fieldsArg , true ) ;
2015-10-13 23:21:31 +02:00
if ( fields . size ( ) ! = 1 ) {
2016-06-14 22:54:49 +02:00
cerr < < " Error: Excactly one field needs to be specified. " < < endl ;
2015-04-22 19:33:53 +02:00
return ;
}
MediaFileInfo inputFileInfo ;
try {
// parse tags
inputFileInfo . setPath ( inputFileArg . values ( ) . front ( ) ) ;
inputFileInfo . open ( true ) ;
inputFileInfo . parseTags ( ) ;
2016-07-31 23:22:22 +02:00
( outputFileArg . isPresent ( ) ? cout : cerr ) < < " Extracting " < < fieldsArg . values ( ) . front ( ) < < " of \" " < < inputFileArg . values ( ) . front ( ) < < " \" ... " < < endl ;
2015-04-22 19:33:53 +02:00
auto tags = inputFileInfo . tags ( ) ;
vector < pair < const TagValue * , string > > values ;
// iterate through all tags
for ( const Tag * tag : tags ) {
for ( const auto & fieldDenotation : fields ) {
2016-07-30 23:17:49 +02:00
const auto & value = tag - > value ( fieldDenotation . first . field ) ;
2015-10-13 23:21:31 +02:00
if ( ! value . isEmpty ( ) ) {
values . emplace_back ( & value , joinStrings ( { tag - > typeName ( ) , numberToString ( values . size ( ) ) } , " - " ) ) ;
2015-04-22 19:33:53 +02:00
}
}
}
if ( values . empty ( ) ) {
2016-06-25 23:11:16 +02:00
cerr < < " File has no (supported) " < < fieldsArg . values ( ) . front ( ) < < " field. " < < endl ;
2016-07-31 23:22:22 +02:00
} else if ( outputFileArg . isPresent ( ) ) {
2015-04-22 19:33:53 +02:00
string outputFilePathWithoutExtension , outputFileExtension ;
if ( values . size ( ) > 1 ) {
outputFilePathWithoutExtension = BasicFileInfo : : pathWithoutExtension ( outputFileArg . values ( ) . front ( ) ) ;
outputFileExtension = BasicFileInfo : : extension ( outputFileArg . values ( ) . front ( ) ) ;
}
for ( const auto & value : values ) {
fstream outputFileStream ;
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 ( ) ;
cout < < " Value has been saved to \" " < < path < < " \" . " < < endl ;
2016-06-14 22:54:49 +02:00
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
cerr < < " Error: An IO error occured when writing the file \" " < < path < < " \" . " < < endl ;
2015-04-22 19:33:53 +02:00
}
}
2016-07-31 23:22:22 +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 ( ) ) ;
}
2015-04-22 19:33:53 +02:00
}
2016-06-14 22:54:49 +02:00
} catch ( const ApplicationUtilities : : Failure & ) {
cerr < < " Error: A parsing failure occured when reading the file \" " < < inputFileArg . values ( ) . front ( ) < < " \" . " < < endl ;
} catch ( . . . ) {
: : IoUtilities : : catchIoFailure ( ) ;
cerr < < " Error: An IO failure occured when reading the file \" " < < inputFileArg . values ( ) . front ( ) < < " \" . " < < endl ;
2015-04-22 19:33:53 +02:00
}
2015-09-23 00:02:06 +02:00
printNotifications ( inputFileInfo , " Parsing notifications: " , verboseArg . isPresent ( ) ) ;
2015-04-22 19:33:53 +02:00
}
}