2015-09-06 20:20:00 +02:00
# include "./htmlinfo.h"
2015-04-22 19:33:53 +02:00
# include <tagparser/signature.h>
# include <tagparser/mediafileinfo.h>
# include <tagparser/abstractcontainer.h>
# include <tagparser/matroska/matroskacontainer.h>
# include <tagparser/matroska/matroskaeditionentry.h>
# include <tagparser/mp4/mp4container.h>
# include <tagparser/tag.h>
# include <tagparser/abstracttrack.h>
# include <tagparser/abstractattachment.h>
# include <tagparser/notification.h>
# include <qtutilities/resources/resources.h>
# include <c++utilities/chrono/timespan.h>
# include <c++utilities/chrono/datetime.h>
# include <c++utilities/conversion/stringconversion.h>
# if defined(GUI_QTWIDGETS)
# include <QApplication>
# include <QStyle>
# elif defined(GUI_QTQUICK)
# include <QGuiApplication>
# elif !defined(GUI_NONE)
# define GUI_NONE
# endif
# ifndef GUI_NONE
# include <QFont>
# include <QFontMetrics>
# include <QIcon>
# endif
# include <QString>
# include <QResource>
# include <QBuffer>
# include <QByteArray>
# include <list>
using namespace std ;
using namespace ConversionUtilities ;
using namespace ChronoUtilities ;
using namespace Media ;
namespace HtmlInfo {
inline QString qstr ( const char * cstr )
{
return QString : : fromLocal8Bit ( cstr ) ;
}
inline QString qstr ( const string & stdstr )
{
return qstr ( stdstr . c_str ( ) ) ;
}
inline QString mkRow ( const QString & label , const QString & text , bool head = true )
{
2015-06-11 00:25:10 +02:00
2015-04-22 19:33:53 +02:00
return head
? QStringLiteral ( " <tr><th>%1</th><td>%2</td></tr> " ) . arg ( label , text )
: QStringLiteral ( " <tr><td>%1</td><td>%2</td></tr> " ) . arg ( label , text ) ;
}
2015-06-11 00:25:10 +02:00
inline QString mkRow ( const QString & label , const QString & helpText , const QString & text )
{
2015-07-07 03:42:38 +02:00
return QStringLiteral ( " <tr title= \" %1 \" ><th class= \" has-helptext \" >%2</th><td>%3</td></tr> " ) . arg ( helpText , label , text ) ;
2015-06-11 00:25:10 +02:00
}
2015-04-22 19:33:53 +02:00
QString mkFontStyle ( const QFont & font )
{
QString style ;
switch ( font . style ( ) ) {
case QFont : : StyleItalic :
style = QStringLiteral ( " italic " ) ;
break ;
case QFont : : StyleOblique :
style = QStringLiteral ( " oblique " ) ;
break ;
default :
style = QStringLiteral ( " normal " ) ;
}
QString weight ;
switch ( font . weight ( ) ) {
case QFont : : Light :
weight = QStringLiteral ( " light " ) ;
break ;
case QFont : : Bold :
weight = QStringLiteral ( " bold " ) ;
break ;
case QFont : : Black :
weight = QStringLiteral ( " bolder " ) ;
break ;
default :
weight = QStringLiteral ( " normal " ) ;
}
QString size ;
if ( font . pixelSize ( ) > 0 ) {
size = QStringLiteral ( " %1px " ) . arg ( font . pixelSize ( ) ) ;
} else {
size = QStringLiteral ( " %1pt " ) . arg ( font . pointSize ( ) ) ;
}
return QStringLiteral ( " font-family: \" %1 \" ; "
" font-style: %2; "
" font-weight: %3; "
" font-size: %4; " ) . arg (
font . family ( ) , style , weight , size ) ;
}
QByteArray mkBase64 ( const QIcon & icon )
{
QByteArray bytes ;
QBuffer buffer ( & bytes ) ;
buffer . open ( QIODevice : : WriteOnly ) ;
icon . pixmap ( 16 , 16 ) . save ( & buffer , " PNG " ) ;
return bytes . toBase64 ( ) ;
}
const QByteArray & mkInfoIconData ( )
{
static const QByteArray data = mkBase64 ( QApplication : : style ( ) - > standardIcon ( QStyle : : SP_MessageBoxInformation ) ) ;
return data ;
}
const QByteArray & mkWarningIconData ( )
{
static const QByteArray data = mkBase64 ( QApplication : : style ( ) - > standardIcon ( QStyle : : SP_MessageBoxWarning ) ) ;
return data ;
}
const QByteArray & mkErrorIconData ( )
{
static const QByteArray data = mkBase64 ( QApplication : : style ( ) - > standardIcon ( QStyle : : SP_MessageBoxCritical ) ) ;
return data ;
}
const QByteArray & mkDebugIconData ( )
{
static const QByteArray data = mkBase64 ( QIcon ( QStringLiteral ( " /images/bug " ) ) ) ;
return data ;
}
QString mkMoreLink ( const QString & id , const QString & text )
{
return QStringLiteral ( " <a href= \" # \" onclick= \" toggleVisibility(this, '%1') ; return false ; \ " >%2</a> " ) . arg ( id , text ) ;
}
QString mkExtendedSection ( const QString & id )
{
return QStringLiteral ( " </tbody><tbody id= \" %1 \" class= \" more \" > " ) . arg ( id ) ;
}
QString mkSection ( )
{
return QStringLiteral ( " </tbody><tbody> " ) ;
}
2015-08-08 02:33:31 +02:00
QString mkSection ( const QString & id )
{
return QStringLiteral ( " </tbody><tbody id= \" %1 \" > " ) . arg ( id ) ;
}
2015-04-22 19:33:53 +02:00
void mkTrack ( QByteArray & res , const AbstractTrack * track , unsigned int trackNumber )
{
res . append ( QStringLiteral ( " <tr><th>%1 #%2</th><td><table class= \" headervertical \" ><tbody> " ) . arg ( QCoreApplication : : translate ( " HtmlInfo " , " Track " ) , QString : : number ( trackNumber ) ) ) ;
if ( track - > id ( ) ) {
2015-06-11 00:25:10 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " ID " ) , QCoreApplication : : translate ( " HtmlInfo " , " The unique number used to identify the track in the container file. " ) , QString : : number ( track - > id ( ) ) ) ) ;
2015-04-22 19:33:53 +02:00
}
if ( track - > trackNumber ( ) ) {
2015-06-11 00:25:10 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Number " ) , QCoreApplication : : translate ( " HtmlInfo " , " The index of the track in the container file. " ) , QString : : number ( track - > trackNumber ( ) ) ) ) ;
2015-04-22 19:33:53 +02:00
}
if ( ! track - > name ( ) . empty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Name " ) , qstr ( track - > name ( ) ) ) ) ;
}
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Type " ) , qstr ( track - > mediaTypeName ( ) ) ) ) ;
2015-06-07 00:15:47 +02:00
const char * fmtName = track - > formatName ( ) , * fmtAbbr = track - > formatAbbreviation ( ) ;
2015-06-11 00:25:10 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Format " ) , QCoreApplication : : translate ( " HtmlInfo " , " The unabbreviated name of the track's format. " ) , qstr ( fmtName ) ) ) ;
2015-06-07 00:15:47 +02:00
if ( strcmp ( fmtName , fmtAbbr ) ) {
2015-06-11 00:25:10 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Abbreviation " ) , QCoreApplication : : translate ( " HtmlInfo " , " The abbreviated name of the track's format. " ) , qstr ( fmtAbbr ) ) ) ;
}
if ( ! track - > formatId ( ) . empty ( ) ) {
2015-07-07 03:42:38 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Format/codec ID " ) , QCoreApplication : : translate ( " HtmlInfo " , " The raw format/codec identifier extracted from the container. " ) , qstr ( track - > formatId ( ) ) ) ) ;
2015-06-07 00:15:47 +02:00
}
2015-04-22 19:33:53 +02:00
if ( track - > version ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Version " ) , QString : : number ( track - > version ( ) ) ) ) ;
}
if ( track - > size ( ) ) {
2015-08-09 23:54:29 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Size " ) , qstr ( dataSizeToString ( track - > size ( ) , true ) ) ) ) ;
2015-04-22 19:33:53 +02:00
}
if ( ! track - > duration ( ) . isNull ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Duration " ) , qstr ( track - > duration ( ) . toString ( TimeSpanOutputFormat : : WithMeasures ) ) ) ) ;
}
if ( track - > bitrate ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Avg. bitrate " ) , qstr ( bitrateToString ( track - > bitrate ( ) ) ) ) ) ;
}
2015-06-11 00:25:10 +02:00
if ( track - > maxBitrate ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Maximum bitrate " ) , qstr ( bitrateToString ( track - > maxBitrate ( ) ) ) ) ) ;
}
2015-04-22 19:33:53 +02:00
if ( ! track - > creationTime ( ) . isNull ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Creation time " ) , qstr ( track - > creationTime ( ) . toString ( DateTimeOutputFormat : : DateAndTime , true ) ) ) ) ;
}
if ( ! track - > modificationTime ( ) . isNull ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Modification time " ) , qstr ( track - > modificationTime ( ) . toString ( DateTimeOutputFormat : : DateAndTime , true ) ) ) ) ;
}
if ( ! track - > language ( ) . empty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Language " ) , qstr ( track - > language ( ) ) ) ) ;
}
if ( ! track - > compressorName ( ) . empty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Compressor name " ) , qstr ( track - > compressorName ( ) ) ) ) ;
}
2015-07-31 01:10:15 +02:00
if ( track - > samplingFrequency ( ) ) {
if ( track - > extensionSamplingFrequency ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Sampling frequency " ) , QStringLiteral ( " %1 Hz / %2 Hz " ) . arg ( track - > extensionSamplingFrequency ( ) ) . arg ( track - > samplingFrequency ( ) ) ) ) ;
2015-06-11 00:25:10 +02:00
} else {
2015-07-31 01:10:15 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Sampling frequency " ) , QStringLiteral ( " %1 Hz " ) . arg ( track - > samplingFrequency ( ) ) ) ) ;
2015-06-11 00:25:10 +02:00
}
2015-04-22 19:33:53 +02:00
}
if ( track - > sampleCount ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Sample count " ) , QString : : number ( track - > sampleCount ( ) ) ) ) ;
}
if ( track - > bitsPerSample ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Bits per sample " ) , QString : : number ( track - > bitsPerSample ( ) ) ) ) ;
}
if ( track - > quality ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Quality " ) , QString : : number ( track - > quality ( ) ) ) ) ;
}
if ( ! track - > pixelSize ( ) . isNull ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Pixel size " ) , qstr ( track - > pixelSize ( ) . toString ( ) ) ) ) ;
}
if ( ! track - > displaySize ( ) . isNull ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Display size " ) , qstr ( track - > displaySize ( ) . toString ( ) ) ) ) ;
}
if ( ! track - > cropping ( ) . isNull ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Cropping " ) , qstr ( track - > cropping ( ) . toString ( ) ) ) ) ;
}
if ( ! track - > resolution ( ) . isNull ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Resolution " ) , qstr ( track - > resolution ( ) . toString ( ) ) ) ) ;
}
2015-07-31 01:10:15 +02:00
if ( track - > channelConfigString ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Channel config " ) , QCoreApplication : : translate ( " HtmlInfo " , " Channel configuration " ) , QString : : fromLocal8Bit ( track - > channelConfigString ( ) ) ) ) ;
} else if ( track - > channelCount ( ) ) {
2015-07-07 03:42:38 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Channel count " ) , QString : : number ( track - > channelCount ( ) ) ) ) ;
}
2015-04-22 19:33:53 +02:00
if ( track - > depth ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Bit depth " ) , QString : : number ( track - > depth ( ) ) ) ) ;
}
if ( track - > fps ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Frames per second " ) , QString : : number ( track - > fps ( ) ) ) ) ;
}
list < string > labels ;
if ( track - > isInterlaced ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " interlaced " ) ;
2015-04-22 19:33:53 +02:00
}
if ( ! track - > isEnabled ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " disabled " ) ;
2015-04-22 19:33:53 +02:00
}
if ( track - > isDefault ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " default " ) ;
2015-04-22 19:33:53 +02:00
}
if ( track - > isForced ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " forced " ) ;
2015-04-22 19:33:53 +02:00
}
if ( track - > hasLacing ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " has lacing " ) ;
2015-04-22 19:33:53 +02:00
}
if ( track - > isEncrypted ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " encrypted " ) ;
2015-04-22 19:33:53 +02:00
}
if ( labels . size ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Labeled as " ) , qstr ( joinStrings ( labels , " , " ) ) ) ) ;
}
res . append ( QStringLiteral ( " </tbody></table></td></tr> " ) ) ;
}
void mkAttachment ( QByteArray & res , const AbstractAttachment * attachment , unsigned int attachmentNumber )
{
res . append ( QStringLiteral ( " <tr><th>%1 #%2</th><td><table class= \" headervertical \" ><tbody> " ) . arg ( QCoreApplication : : translate ( " HtmlInfo " , " Attachment " ) , QString : : number ( attachmentNumber ) ) ) ;
if ( attachment - > id ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " ID " ) , QString : : number ( attachment - > id ( ) ) ) ) ;
}
if ( ! attachment - > name ( ) . empty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Name " ) , qstr ( attachment - > name ( ) ) ) ) ;
}
if ( attachment - > data ( ) ) {
2015-08-09 23:54:29 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Size " ) , qstr ( dataSizeToString ( attachment - > data ( ) - > size ( ) , true ) ) ) ) ;
2015-04-22 19:33:53 +02:00
}
if ( ! attachment - > mimeType ( ) . empty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Mime-type " ) , qstr ( attachment - > mimeType ( ) ) ) ) ;
}
if ( ! attachment - > description ( ) . empty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Description " ) , qstr ( attachment - > description ( ) ) ) ) ;
}
res . append ( QStringLiteral ( " </tbody></table></td></tr> " ) ) ;
}
void mkChapter ( QByteArray & res , const AbstractChapter & chapter , unsigned int chapterNumber , unsigned int level = 0 )
{
if ( level ) {
res . append ( QStringLiteral ( " <tr><th>%1 #%2<br />level %3</th><td><table class= \" headervertical \" style= \" margin-left: %4px \" ><tbody> " ) . arg ( QCoreApplication : : translate ( " HtmlInfo " , " Chapter " ) , QString : : number ( chapterNumber + 1 ) , QString : : number ( level + 1 ) , QString : : number ( level * 20 ) ) ) ;
} else {
res . append ( QStringLiteral ( " <tr><th>%1 #%2</th><td><table class= \" headervertical \" ><tbody> " ) . arg ( QCoreApplication : : translate ( " HtmlInfo " , " Chapter " ) , QString : : number ( chapterNumber + 1 ) ) ) ;
}
if ( chapter . id ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " ID " ) , QString : : number ( chapter . id ( ) ) ) ) ;
}
for ( const LocaleAwareString & name : chapter . names ( ) ) {
static const string delim ( " , " ) ;
string locale = joinStrings ( initializer_list < string > { joinStrings ( name . languages ( ) , delim , true ) , joinStrings ( name . countries ( ) , delim , true ) } , delim , true ) ;
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Name (%1) " ) . arg ( qstr ( locale ) ) , qstr ( name ) ) ) ;
}
if ( chapter . startTime ( ) . totalTicks ( ) > 0 ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Start time " ) , qstr ( chapter . startTime ( ) . toString ( TimeSpanOutputFormat : : WithMeasures ) ) ) ) ;
}
if ( chapter . endTime ( ) . totalTicks ( ) > 0 ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " End time " ) , qstr ( chapter . endTime ( ) . toString ( TimeSpanOutputFormat : : WithMeasures ) ) ) ) ;
}
list < string > labels ;
if ( chapter . isHidden ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " hidden " ) ;
2015-04-22 19:33:53 +02:00
}
if ( ! chapter . isEnabled ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " disabled " ) ;
2015-04-22 19:33:53 +02:00
}
if ( labels . size ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Labeled as " ) , qstr ( joinStrings ( labels , " , " ) ) ) ) ;
}
if ( chapter . tracks ( ) . size ( ) ) {
QStringList trackIds ;
for ( uint64 id : chapter . tracks ( ) ) {
trackIds < < QString : : number ( id ) ;
}
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Tracks " ) , trackIds . join ( QStringLiteral ( " , " ) ) ) ) ;
}
res . append ( QStringLiteral ( " </tbody></table></td></tr> " ) ) ;
+ + level ;
for ( size_t i = 0 , nestedChapters = chapter . nestedChapterCount ( ) ; i < nestedChapters ; + + i ) {
mkChapter ( res , * chapter . nestedChapter ( i ) , i , level ) ;
}
}
void mkEdition ( QByteArray & res , const MatroskaEditionEntry & edition , unsigned int editionNumber )
{
res . append ( QStringLiteral ( " <tr><th>%1 #%2</th><td><table class= \" headervertical \" ><tbody> " ) . arg ( QCoreApplication : : translate ( " HtmlInfo " , " Edition " ) , QString : : number ( editionNumber + 1 ) ) ) ;
if ( edition . id ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " ID " ) , QString : : number ( edition . id ( ) ) ) ) ;
}
list < string > labels ;
if ( edition . isHidden ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " hidden " ) ;
2015-04-22 19:33:53 +02:00
}
if ( edition . isDefault ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " default " ) ;
2015-04-22 19:33:53 +02:00
}
if ( edition . isOrdered ( ) ) {
2015-09-06 23:42:24 +02:00
labels . push_back ( " ordered " ) ;
2015-04-22 19:33:53 +02:00
}
if ( labels . size ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Labeled as " ) , qstr ( joinStrings ( labels , " , " ) ) ) ) ;
}
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Chapters " ) ,
QCoreApplication : : translate ( " HtmlInfo " , " edition contains %1 chapter(s) " , nullptr , edition . chapters ( ) . size ( ) ) . arg ( edition . chapters ( ) . size ( ) ) ) ) ;
res . append ( QStringLiteral ( " </tbody></table></td></tr> " ) ) ;
unsigned int chapterNumber = 0 ;
for ( const auto & chapter : edition . chapters ( ) ) {
mkChapter ( res , * chapter , chapterNumber ) ;
+ + chapterNumber ;
}
}
template < class ElementType > void mkElementNode ( QByteArray & res , const ElementType * element )
{
if ( element & & element - > parent ( ) ) {
res . append ( QStringLiteral ( " <ul class= \" nodecollapsed \" > " ) ) ;
} else {
res . append ( QStringLiteral ( " <ul class= \" nodeexpanded \" > " ) ) ;
}
while ( element ) {
if ( element - > isParsed ( ) ) {
res . append ( QStringLiteral ( " <li> " ) ) ;
if ( element - > firstChild ( ) ) {
2015-08-08 02:33:31 +02:00
res . append ( QStringLiteral ( " <span class= \" parent-node \" onclick= \" expandCollapse(this.parentElement); \" > " ) ) ;
2015-04-22 19:33:53 +02:00
}
2015-08-08 02:33:31 +02:00
res . append ( QCoreApplication : : translate ( " HtmlInfo " , " <em>%1</em> @<span data-role= \" offset \" data-dec= \" %2 \" data-hex= \" 0x%3 \" >%2</span>, size: <span data-role= \" offset \" data-dec= \" %4 \" data-hex= \" 0x%5 \" >%4</span> " ) . arg (
QString : : fromLatin1 ( element - > idToString ( ) . c_str ( ) ) , QString : : number ( element - > startOffset ( ) ) , QString : : number ( element - > startOffset ( ) , 16 ) ,
QString : : number ( element - > totalSize ( ) ) , QString : : number ( element - > totalSize ( ) , 16 ) ) ) ;
2015-04-22 19:33:53 +02:00
if ( element - > firstChild ( ) ) {
2015-08-08 02:33:31 +02:00
res . append ( QStringLiteral ( " </span> " ) ) ;
2015-04-22 19:33:53 +02:00
mkElementNode ( res , element - > firstChild ( ) ) ;
}
res . append ( QStringLiteral ( " </li> " ) ) ;
element = element - > nextSibling ( ) ;
} else {
res . append ( QStringLiteral ( " <li style= \" color: red; \" > " ) ) ;
res . append ( QCoreApplication : : translate ( " HtmlInfo " , " section has not been analyzed " ) ) ;
res . append ( QStringLiteral ( " </li> " ) ) ;
break ;
}
}
res . append ( QStringLiteral ( " </ul> " ) ) ;
}
template < class ContainerType > void mkElementTree ( QByteArray & res , const ContainerType * container )
{
mkElementNode ( res , container - > firstElement ( ) ) ;
for ( const auto & element : container - > additionalElements ( ) ) {
mkElementNode ( res , element . get ( ) ) ;
}
}
void mkNotifications ( QByteArray & res , NotificationList & notifications , bool reparsing = false )
{
if ( notifications . size ( ) ) {
res . append ( mkSection ( ) ) ;
QString moreId = reparsing ? QStringLiteral ( " notificationsReparsingMore " ) : QStringLiteral ( " notificationsMore " ) ;
res . append ( mkRow ( reparsing ? QCoreApplication : : translate ( " HtmlInfo " , " Notifications (reparsing) " ) : QCoreApplication : : translate ( " HtmlInfo " , " Notifications " ) ,
QCoreApplication : : translate ( " HtmlInfo " , " %1 notification(s) available " , 0 , notifications . size ( ) ) . arg ( notifications . size ( ) )
+ mkMoreLink ( moreId , QCoreApplication : : translate ( " HtmlInfo " , " show notifications " ) ) ) ) ;
res . append ( mkExtendedSection ( moreId ) ) ;
res . append ( QStringLiteral ( " <tr><th></th><td><table class= \" headerhorizontal \" ><thead><tr><th></th><th>%1</th><th>%2</th><th>%3</th></tr></thead><tbody> " ) . arg (
2015-09-06 23:42:24 +02:00
QCoreApplication : : translate ( " HtmlInfo " , " Context " ) , QCoreApplication : : translate ( " HtmlInfo " , " Message " ) , QCoreApplication : : translate ( " HtmlInfo " , " Time " ) ) ) ;
2015-04-22 19:33:53 +02:00
Notification : : sortByTime ( notifications ) ;
for ( const Notification & notification : notifications ) {
res . append ( QStringLiteral ( " <tr><td class= \" %4 \" ></td><td>%1</td><td>%2</td><td>%3</td></tr> " ) . arg (
2015-09-06 23:42:24 +02:00
qstr ( notification . context ( ) ) , qstr ( notification . message ( ) ) ,
qstr ( notification . creationTime ( ) . toString ( DateTimeOutputFormat : : DateAndTime , false ) ) ,
qstr ( notification . typeName ( ) ) ) ) ;
2015-04-22 19:33:53 +02:00
}
res . append ( QStringLiteral ( " </tbody></table></td></tr> " ) ) ;
}
}
/*!
* \ brief Generates technical information for the specified \ a file .
*
2015-07-07 23:03:49 +02:00
* The parse methods of the \ a file must have already been called .
2015-04-22 19:33:53 +02:00
*
* A QGuiApplication instance should be available for setting fonts .
* A QApplication instance should be available for standard icons .
*/
QByteArray generateInfo ( const MediaFileInfo & file , NotificationList & originalNotifications )
{
QByteArray res ;
// header
res . append ( QStringLiteral ( " <!DOCTYPE html> \n "
" <html><head> "
" <title>Info for " ) ) ;
res . append ( qstr ( file . fileName ( ) ) ) ;
res . append ( QStringLiteral ( " </title> "
" <style type= \" text/css \" > "
" html, body { "
" padding: 1px; "
2015-09-06 23:42:24 +02:00
" margin: 0px; " ) ) ;
2015-04-22 19:33:53 +02:00
# ifndef GUI_NONE
if ( ApplicationInstances : : hasGuiApp ( ) ) {
res . append ( mkFontStyle ( QGuiApplication : : font ( ) ) ) ;
}
2015-09-06 23:42:24 +02:00
const QPalette palette = QGuiApplication : : palette ( ) ;
res . append ( QStringLiteral ( " background-color: %1; " ) . arg ( palette . color ( QPalette : : Base ) . name ( ) ) ) ;
res . append ( QStringLiteral ( " color: %1; " ) . arg ( palette . color ( QPalette : : Text ) . name ( ) ) ) ;
# else
res . append ( QStringLiteral ( " background-color: #fff; "
" color: #000; " ) ) ;
2015-04-22 19:33:53 +02:00
# endif
res . append ( QStringLiteral ( " } "
2015-09-06 23:42:24 +02:00
" a:link, #structure .parent-node { " ) ) ;
# ifndef GUI_NONE
res . append ( QStringLiteral ( " color: %1; " ) . arg ( palette . color ( QPalette : : Link ) . name ( ) ) ) ;
# else
res . append ( QStringLiteral ( " color: #337AB7; " ) ) ;
# endif
res . append ( QStringLiteral ( " text-decoration: none; "
2015-04-22 19:33:53 +02:00
" } "
2015-09-06 23:42:24 +02:00
" a:focus, a:hover { " ) ) ;
# ifndef GUI_NONE
res . append ( QStringLiteral ( " color: %1; " ) . arg ( palette . link ( ) . color ( ) . darker ( palette . color ( QPalette : : Background ) . lightness ( ) > palette . color ( QPalette : : Link ) . lightness ( ) ? 150 : 50 ) . name ( ) ) ) ;
# else
res . append ( QStringLiteral ( " color: #23527c; " ) ) ;
# endif
res . append ( QStringLiteral ( " text-decoration: underline; "
2015-04-22 19:33:53 +02:00
" } "
" table { "
" border-collapse: collapse; "
" width: 100%; "
" } "
2015-09-06 23:42:24 +02:00
" tr:nth-child(2n+2) { " ) ) ;
# ifndef GUI_NONE
res . append ( QStringLiteral ( " background-color: %1; " ) . arg ( palette . color ( QPalette : : AlternateBase ) . name ( ) ) ) ;
# else
res . append ( QStringLiteral ( " background-color: #fafafa; " ) ) ;
# endif
res . append ( QStringLiteral ( " } "
2015-04-22 19:33:53 +02:00
" table.headervertical th, table.headervertical td { "
" padding: 2px 5px; "
" text-align: left; "
" vertical-align: top; "
" } "
" table.headervertical > tbody > tr > th { "
" text-align: right; "
2015-09-06 23:42:24 +02:00
" border: none; "
2015-08-01 20:39:19 +02:00
" width: 25%; "
2015-04-22 19:33:53 +02:00
" } "
" table.headerhorizontal > thead > tr > th { "
" text-align: center; "
2015-09-06 23:42:24 +02:00
" border: none; "
2015-04-22 19:33:53 +02:00
" } "
" .more { "
" display: none; "
" } "
" .more tr > th { "
" font-weight: normal; "
" } "
" .nodecollapsed { "
" display: none; "
" } "
" .nodeexpaned { "
" display: block; "
" } "
" table.headerhorizontal > thead > tr > th { "
" font-weight: bold; "
" } "
" td table.headervertical > thead > tr > th { "
" font-weight: normal; "
" border: none; "
" background-color: transparent; "
" } "
2015-07-07 03:42:38 +02:00
" th { "
" cursor: default; "
" } "
2015-04-22 19:33:53 +02:00
" td.warning, td.critical, td.information { "
" width: 18px; "
" background-repeat: no-repeat; "
" background-position:center; "
2015-07-07 03:42:38 +02:00
" } "
" .has-helptext { "
" cursor: help; "
2015-08-08 02:33:31 +02:00
" } "
" #structure_links a { "
" margin-right: 5px; "
2015-04-22 19:33:53 +02:00
" } " ) ) ;
# ifdef GUI_QTWIDGETS
if ( ApplicationInstances : : hasWidgetsApp ( ) ) {
res . append ( QStringLiteral ( " td.warning {background-image: url(data:image/png;base64, " ) ) ;
res . append ( mkWarningIconData ( ) ) ;
res . append ( QStringLiteral ( " );}td.critical {background-image: url(data:image/png;base64, " ) ) ;
res . append ( mkErrorIconData ( ) ) ;
res . append ( QStringLiteral ( " );}td.information {background-image: url(data:image/png;base64, " ) ) ;
res . append ( mkInfoIconData ( ) ) ;
res . append ( QStringLiteral ( " );} " ) ) ;
}
# endif
res . append ( QStringLiteral ( " ul { "
" margin: 0px; "
" padding: 0px 0px 0px 15px; "
" list-style-type: none; "
" </style> "
" <script type= \" text/javascript \" > "
" function toggleVisibility(link, objid) { "
" var obj = document.getElementById(objid); "
2015-08-08 02:33:31 +02:00
" if(obj.style.display === \" none \" || (obj.style.display === \" \" && obj.className === \" more \" )) { "
" if(obj.nodeName === \" TBODY \" ) { "
2015-04-22 19:33:53 +02:00
" obj.style.display = \" table-row-group \" ; "
" } else { "
" obj.style.display = \" block \" ; "
" } "
" link.defaultText = link.innerHTML; "
" link.innerHTML = \" " ) ) ;
res . append ( QCoreApplication : : translate ( " HtmlInfo " , " hide " ) ) ;
res . append ( QStringLiteral ( " \" ; "
" } else { "
" obj.style.display = \" none \" ; "
" link.innerHTML = link.defaultText; "
" }} "
" function expandCollapse(parent) { "
" for(var children = parent.children, i = 0, c = children.length; i < c; ++i) { "
2015-08-08 02:33:31 +02:00
" if(children[i].tagName === 'UL') { "
" children[i].className = children[i].className === 'nodeexpanded' ? 'nodecollapsed' : 'nodeexpanded'; "
2015-04-22 19:33:53 +02:00
" }}} "
" function expandAll(objid) { "
" var children = document.getElementById(objid).getElementsByTagName('ul'); "
" for(var i = 0; i < children.length; ++i) { "
" children[i].className = 'nodeexpanded'; "
" }} "
" function collapseAll(objid) { "
" var children = document.getElementById(objid).getElementsByTagName('ul'); "
" for(var i = 0; i < children.length; ++i) { "
2015-08-08 02:33:31 +02:00
" if(children[i].parentNode.tagName === 'LI') { "
2015-04-22 19:33:53 +02:00
" children[i].className = 'nodecollapsed'; "
" }}} "
2015-08-08 02:33:31 +02:00
" var baseHex = false; "
" function switchBase(objid) { "
" baseHex = !baseHex; "
" document.getElementById('toggle_hex_link').firstChild.data = baseHex ? 'decimal' : 'hex'; "
" var elements = document.getElementById(objid).getElementsByTagName('span'); "
" for(var i = 0; i < elements.length; ++i) { "
" var element = elements[i]; "
" if(element.getAttribute('data-role') === 'offset') { "
" element.firstChild.data = element.getAttribute(baseHex ? 'data-hex' : 'data-dec'); "
" }}} "
2015-04-22 19:33:53 +02:00
" </script></head> "
" <body><table class= \" headervertical \" ><tbody id= \" general \" > " ) ) ;
// general information
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Path " ) , qstr ( file . path ( ) ) ) ) ;
2015-08-09 23:54:29 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Size " ) , qstr ( dataSizeToString ( file . size ( ) , true ) ) ) ) ;
2015-04-22 19:33:53 +02:00
TimeSpan duration = file . duration ( ) ;
if ( ! duration . isNull ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Duration " ) , qstr ( duration . toString ( TimeSpanOutputFormat : : WithMeasures ) ) ) ) ;
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Overall avg. bitrate " ) , qstr ( bitrateToString ( 0.0078125 * file . size ( ) / duration . totalSeconds ( ) ) ) ) ) ;
}
const char * mimeType = file . mimeType ( ) ;
if ( * mimeType ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Mime-type " ) , qstr ( mimeType ) ) ) ;
}
AbstractContainer * container = file . container ( ) ;
QString containerInfo = qstr ( file . containerFormatName ( ) ) ;
const char * subversion = file . containerFormatSubversion ( ) ;
if ( * subversion ) {
containerInfo . append ( ' ' ) ;
containerInfo . append ( qstr ( subversion ) ) ;
}
if ( container | | file . paddingSize ( ) ) {
containerInfo . append ( mkMoreLink ( QStringLiteral ( " containerMore " ) , QCoreApplication : : translate ( " HtmlInfo " , " show details " ) ) ) ;
}
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Container " ) , containerInfo ) ) ;
// container details
if ( container | | file . paddingSize ( ) ) {
res . append ( mkExtendedSection ( QStringLiteral ( " containerMore " ) ) ) ;
}
if ( container ) {
if ( ! container - > title ( ) . empty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Title " ) , qstr ( container - > title ( ) ) ) ) ;
}
if ( container - > version ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Version " ) , QString : : number ( container - > version ( ) ) ) ) ;
}
if ( container - > readVersion ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Read version " ) , QString : : number ( container - > readVersion ( ) ) ) ) ;
}
if ( ! container - > documentType ( ) . empty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Document type " ) , qstr ( container - > documentType ( ) ) ) ) ;
}
if ( container - > doctypeVersion ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Document version " ) , QString : : number ( container - > doctypeVersion ( ) ) ) ) ;
}
if ( container - > doctypeReadVersion ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Document read version " ) , QString : : number ( container - > doctypeReadVersion ( ) ) ) ) ;
}
}
if ( file . paddingSize ( ) ) {
2015-08-09 23:54:29 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Padding size " ) , QStringLiteral ( " %1 (%2 %) " ) . arg ( qstr ( dataSizeToString ( file . paddingSize ( ) , true ) ) ) . arg ( static_cast < double > ( file . paddingSize ( ) ) / file . size ( ) * 100.0 , 0 , ' g ' , 2 ) ) ) ;
2015-04-22 19:33:53 +02:00
}
// tags
vector < Tag * > tags = file . tags ( ) ;
if ( tags . size ( ) ) {
res . append ( mkSection ( ) ) ;
QString moreId = QStringLiteral ( " tagsMore " ) ;
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Tags " ) , QCoreApplication : : translate ( " HtmlInfo " , " %1 tag(s) assigned, " , 0 , tags . size ( ) ) . arg ( tags . size ( ) ) + mkMoreLink ( moreId , QCoreApplication : : translate ( " HtmlInfo " , " show details " ) ) ) ) ;
res . append ( mkExtendedSection ( moreId ) ) ;
for ( const Tag * tag : tags ) {
res . append ( QStringLiteral ( " <tr><th>%1</th><td><table class= \" headervertical \" ><tbody> " ) . arg ( tag - > typeName ( ) ) ) ;
if ( ! tag - > version ( ) . empty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Version " ) , qstr ( tag - > version ( ) ) ) ) ;
}
if ( tag - > supportsTarget ( ) & & ! tag - > target ( ) . isEmpty ( ) ) {
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Target level " ) , qstr ( tag - > target ( ) . toString ( ) ) ) ) ;
}
2015-08-09 23:54:29 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Size " ) , qstr ( dataSizeToString ( tag - > size ( ) , true ) ) ) ) ;
2015-04-22 19:33:53 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Field count " ) , QString : : number ( tag - > fieldCount ( ) ) ) ) ;
res . append ( QStringLiteral ( " </tbody></table></td></tr> " ) ) ;
}
}
// tracks
auto tracks = file . tracks ( ) ;
if ( tracks . size ( ) ) {
res . append ( mkSection ( ) ) ;
QString moreId = QStringLiteral ( " tracksMore " ) ;
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Tracks " ) , QCoreApplication : : translate ( " HtmlInfo " , " file contains %1 track(s), " , 0 , tracks . size ( ) ) . arg ( tracks . size ( ) )
+ mkMoreLink ( moreId , QCoreApplication : : translate ( " HtmlInfo " , " show details " ) ) ) ) ;
res . append ( mkExtendedSection ( moreId ) ) ;
int trackNumber = 1 ;
for ( const auto * track : tracks ) {
mkTrack ( res , track , trackNumber ) ;
+ + trackNumber ;
}
}
auto attachments = file . attachments ( ) ;
if ( attachments . size ( ) ) {
res . append ( mkSection ( ) ) ;
QString moreId = QStringLiteral ( " attachmentsMore " ) ;
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Attachments " ) , QCoreApplication : : translate ( " HtmlInfo " , " file contains %1 attachment(s), " , 0 , tracks . size ( ) ) . arg ( attachments . size ( ) )
+ mkMoreLink ( moreId , QCoreApplication : : translate ( " HtmlInfo " , " show details " ) ) ) ) ;
res . append ( mkExtendedSection ( moreId ) ) ;
int attachmentNumber = 1 ;
for ( const auto * attachment : attachments ) {
mkAttachment ( res , attachment , attachmentNumber ) ;
+ + attachmentNumber ;
}
}
// chapters
if ( container ) {
if ( file . containerFormat ( ) = = ContainerFormat : : Matroska ) {
if ( size_t editionCount = static_cast < const MatroskaContainer * > ( container ) - > editionEntires ( ) . size ( ) ) {
res . append ( mkSection ( ) ) ;
QString moreId = QStringLiteral ( " editionsMore " ) ;
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Editions/chapters " ) , QCoreApplication : : translate ( " HtmlInfo " , " file contains %1 edition(s), " , 0 , editionCount ) . arg ( editionCount )
+ mkMoreLink ( moreId , QCoreApplication : : translate ( " HtmlInfo " , " show details " ) ) ) ) ;
res . append ( mkExtendedSection ( moreId ) ) ;
unsigned int editionNumber = 0 ;
for ( const auto & edition : static_cast < const MatroskaContainer * > ( container ) - > editionEntires ( ) ) {
mkEdition ( res , * edition , editionNumber ) ;
+ + editionNumber ;
}
}
} else if ( size_t chapterCount = container - > chapterCount ( ) ) {
res . append ( mkSection ( ) ) ;
QString moreId = QStringLiteral ( " chaptersMore " ) ;
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Chapters " ) , QCoreApplication : : translate ( " HtmlInfo " , " file contains %1 chapter(s), " , 0 , chapterCount ) . arg ( chapterCount )
+ mkMoreLink ( moreId , QCoreApplication : : translate ( " HtmlInfo " , " show details " ) ) ) ) ;
res . append ( mkExtendedSection ( moreId ) ) ;
for ( size_t i = 0 ; i < chapterCount ; + + i ) {
mkChapter ( res , * container - > chapter ( i ) , i ) ;
}
}
}
// structure
if ( file . containerFormat ( ) = = ContainerFormat : : Mp4 | | file . containerFormat ( ) = = ContainerFormat : : Matroska ) {
res . append ( mkSection ( ) ) ;
2015-08-08 02:33:31 +02:00
res . append ( mkRow ( QCoreApplication : : translate ( " HtmlInfo " , " Structure " ) ,
QCoreApplication : : translate ( " HtmlInfo " , " <span id= \" structure_links \" ><a href= \" javascript: expandAll('structure'); \" >expand all</a> <a href= \" javascript: collapseAll('structure'); \" >collapse all</a> "
" <a id= \" toggle_hex_link \" href= \" javascript: switchBase('structure'); \" >hex</a></span> " ) ) ) ;
2015-04-22 19:33:53 +02:00
res . append ( QStringLiteral ( " </tbody><tbody id= \" structure \" ><tr><th></th><td> " ) ) ;
switch ( file . containerFormat ( ) ) {
case ContainerFormat : : Mp4 :
mkElementTree ( res , static_cast < Mp4Container * > ( file . container ( ) ) ) ;
break ;
case ContainerFormat : : Matroska :
mkElementTree ( res , static_cast < MatroskaContainer * > ( file . container ( ) ) ) ;
break ;
default :
;
}
res . append ( " </td></tr> " ) ;
}
// notifications
NotificationList currentNotifications = file . gatherRelatedNotifications ( ) ;
mkNotifications ( res , currentNotifications , originalNotifications . size ( ) ) ;
if ( originalNotifications . size ( ) ) {
mkNotifications ( res , originalNotifications ) ;
}
// end
res . append ( QStringLiteral ( " </tbody></table></body></html> " ) ) ;
return res ;
}
}