Drop Qt dependency when building only CLI

This commit is contained in:
Martchus 2016-07-27 18:31:42 +02:00
parent cd301090c2
commit aaccbcc375
7 changed files with 114 additions and 56 deletions

View File

@ -2,31 +2,37 @@ cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
# meta data # meta data
set(META_PROJECT_NAME tageditor) set(META_PROJECT_NAME tageditor)
set(META_PROJECT_TYPE application)
set(META_APP_NAME "Tag Editor") set(META_APP_NAME "Tag Editor")
set(META_APP_CATEGORIES "Utility;Audio;Video;") set(META_APP_CATEGORIES "Utility;Audio;Video;")
set(META_APP_AUTHOR "Martchus") set(META_APP_AUTHOR "Martchus")
set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
set(META_APP_DESCRIPTION "A tageditor with Qt GUI and command line interface. Supports MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska.") set(META_APP_DESCRIPTION "A tageditor with Qt GUI and command line interface. Supports MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska")
set(META_VERSION_MAJOR 1) set(META_VERSION_MAJOR 1)
set(META_VERSION_MINOR 4) set(META_VERSION_MINOR 4)
set(META_VERSION_PATCH 1) set(META_VERSION_PATCH 1)
# add project files # add project files
set(HEADER_FILES set(HEADER_FILES
application/knownfieldmodel.h
application/targetlevelmodel.h
application/main.h application/main.h
application/settings.h
cli/mainfeatures.h cli/mainfeatures.h
application/knownfieldmodel.h
)
set(SRC_FILES
application/main.cpp
cli/mainfeatures.cpp
application/knownfieldmodel.cpp
)
set(GUI_HEADER_FILES
application/targetlevelmodel.h
application/settings.h
misc/htmlinfo.h misc/htmlinfo.h
misc/utility.h misc/utility.h
) )
set(SRC_FILES set(GUI_SRC_FILES
application/knownfieldmodel.cpp
application/targetlevelmodel.cpp application/targetlevelmodel.cpp
application/main.cpp
application/settings.cpp application/settings.cpp
cli/mainfeatures.cpp
misc/htmlinfo.cpp misc/htmlinfo.cpp
misc/utility.cpp misc/utility.cpp
) )
@ -180,8 +186,10 @@ find_package(c++utilities 4.0.0 REQUIRED)
use_cpp_utilities() use_cpp_utilities()
# find qtutilities # find qtutilities
find_package(qtutilities 4.0.0 REQUIRED) if(WIDGETS_GUI OR QUICK_GUI)
use_qt_utilities() find_package(qtutilities 5.0.0 REQUIRED)
use_qt_utilities()
endif()
# find tagparser # find tagparser
find_package(tagparser 6.0.0 REQUIRED) find_package(tagparser 6.0.0 REQUIRED)
@ -192,10 +200,12 @@ list(APPEND ADDITIONAL_QT_MODULES Concurrent Network)
# include modules to apply configuration # include modules to apply configuration
include(BasicConfig) include(BasicConfig)
include(QtGuiConfig) if(WIDGETS_GUI OR QUICK_GUI)
include(JsProviderConfig) include(QtGuiConfig)
include(WebViewProviderConfig) include(JsProviderConfig)
include(QtConfig) include(WebViewProviderConfig)
include(QtConfig)
endif()
include(WindowsResources) include(WindowsResources)
include(AppTarget) include(AppTarget)
include(ShellCompletion) include(ShellCompletion)

View File

@ -36,7 +36,7 @@ However, it is also possible to force rewriting the entire file.
Taking advantage of padding is currently not supported when dealing with Ogg streams (it is supported when Taking advantage of padding is currently not supported when dealing with Ogg streams (it is supported when
dealing with raw FLAC streams). dealing with raw FLAC streams).
## Download / repository ## Download / binary repository
I currently provide packages for Arch Linux and Windows. For more information checkout my I currently provide packages for Arch Linux and Windows. For more information checkout my
[website](http://martchus.no-ip.biz/website/page.php?name=programming). [website](http://martchus.no-ip.biz/website/page.php?name=programming).
@ -71,27 +71,30 @@ you will see a preview with the generated file names.
The tag editor also features a MusicBrainz and Cover Art Archive search which can be opened with *F10*. However, this feature is still experimental. The tag editor also features a MusicBrainz and Cover Art Archive search which can be opened with *F10*. However, this feature is still experimental.
### CLI ### CLI
Usage: #### Usage
``` ```
tageditor <operation> [options] tageditor <operation> [options]
``` ```
Checkout the available operations and options with --help. Checkout the available operations and options with --help.
#### Examples
Here are some Bash examples which illustrate getting and setting tag information: Here are some Bash examples which illustrate getting and setting tag information:
* Displays title, album and artist of all *.m4a files in the specified directory: * *Displays* title, album and artist of all *.m4a files in the specified directory:
``` ```
tageditor get title album artist --files /some/dir/*.m4a tageditor get title album artist --files /some/dir/*.m4a
``` ```
* Displays technical information about all *.m4a files in the specified directory: **Note**: All values are printed in UTF-8 encoding, no matter which encoding is actually used within the tag.
* *Displays* technical information about all *.m4a files in the specified directory:
``` ```
tageditor info --files /some/dir/*.m4a tageditor info --files /some/dir/*.m4a
``` ```
* Sets title, album, artist, cover and track number of all *.m4a files in the specified directory: * *Sets* title, album, artist, cover and track number of all *.m4a files in the specified directory:
``` ```
tageditor set "title=Title of "{1st,2nd,3rd}" file" "title=Title of "{4..16}"th file" \ tageditor set "title=Title of "{1st,2nd,3rd}" file" "title=Title of "{4..16}"th file" \
@ -103,6 +106,8 @@ Here are some Bash examples which illustrate getting and setting tag information
The 16th and following files will all get the name *Title of the 16th file*. The same scheme is used for the track numbers. The 16th and following files will all get the name *Title of the 16th file*. The same scheme is used for the track numbers.
All files will get the album name *The Album*, the artist *The Artist* and the cover image from the file */path/to/image*. All files will get the album name *The Album*, the artist *The Artist* and the cover image from the file */path/to/image*.
**Note**: All specified values are assumed to be UTF-8 encoded, no matter which encoding has been specified as preferred encoding via ``--encoding`` option. (This mentioned option only affects the encoding to be used *within* the tag.)
* Here is another example, demonstrating the use of arrays and the syntax to auto-increase numeric fields such as the track number: * Here is another example, demonstrating the use of arrays and the syntax to auto-increase numeric fields such as the track number:
``` ```
@ -124,15 +129,24 @@ Here are some Bash examples which illustrate getting and setting tag information
a file has been processed. a file has been processed.
## Build instructions ## Build instructions
The application depends on c++utilities, qtutilities and tagparser and is built in the same way as these libaries The application depends on [c++utilities](https://github.com/Martchus/cpp-utilities) and [tagparser](https://github.com/Martchus/tagparser) and is built the same way as these libaries. For basic instructions checkout the README file of [c++utilities](https://github.com/Martchus/cpp-utilities).
which are also available on my GitHub profile.
### Building with Qt 5 GUI
The following Qt 5 modules are requried: core concurrent gui network declarative/script widgets webenginewidgets/webkitwidgets The following Qt 5 modules are requried: core concurrent gui network declarative/script widgets webenginewidgets/webkitwidgets
#### Select Qt modules for JavaScript and WebView
* If Qt Script is installed on the system, the editor will link against it. Otherwise it will link against Qt QML. * If Qt Script is installed on the system, the editor will link against it. Otherwise it will link against Qt QML.
* To force usage of Qt Script/Qt QML or to disable both add `-DJS_PROVIDER=script/qml/none` to the cmake arguments. * To force usage of Qt Script/Qt QML or to disable both add `-DJS_PROVIDER=script/qml/none` to the CMake arguments.
* If Qt WebKitWidgets is installed on the system, the editor will link against it. Otherwise it will link against Qt WebEngineWidgets. * If Qt WebKitWidgets is installed on the system, the editor will link against it. Otherwise it will link against Qt WebEngineWidgets.
* To force usage of Qt WebKit/Qt WebEngine or to disable both add `-DWEBVIEW_PROVIDER=webkit/webengine/none` to the cmake arguments. * To force usage of Qt WebKit/Qt WebEngine or to disable both add `-DWEBVIEW_PROVIDER=webkit/webengine/none` to the CMake arguments.
### Building without Qt 5 GUI
It is possible to build without the GUI if only the CLI is needed. In this case no Qt dependencies (including qtutilities) are required.
To build without GUI, add the following parameters to the CMake call:
```
-DWIDGETS_GUI=OFF -DQUICK_GUI=OFF
```
## TODO ## TODO
- Support more formats (EXIF, PDF metadata, Theora in Ogg, ...). - Support more formats (EXIF, PDF metadata, Theora in Ogg, ...).

View File

@ -2,9 +2,14 @@
#include <tagparser/tag.h> #include <tagparser/tag.h>
using namespace Models;
using namespace Media; using namespace Media;
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
using namespace Models;
#else
# define QT_TR_NOOP(x) x
#endif
namespace Settings { namespace Settings {
/* /*
@ -47,6 +52,7 @@ const char *KnownFieldModel::fieldName(KnownField field)
} }
} }
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
QString KnownFieldModel::translatedFieldName(KnownField field) QString KnownFieldModel::translatedFieldName(KnownField field)
{ {
return tr(fieldName(field)); return tr(fieldName(field));
@ -118,5 +124,6 @@ QVariant KnownFieldModel::headerData(int section, Qt::Orientation orientation, i
} }
return QVariant(); return QVariant();
} }
#endif
} }

View File

@ -1,10 +1,14 @@
#ifndef KNOWNFIELDMODEL_H #ifndef KNOWNFIELDMODEL_H
#define KNOWNFIELDMODEL_H #define KNOWNFIELDMODEL_H
#include <qtutilities/models/checklistmodel.h> #if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
# include <qtutilities/models/checklistmodel.h>
#include <QAbstractListModel> # include <QAbstractListModel>
#include <QList> # include <QList>
#else
# include <c++utilities/application/global.h>
# define Q_OBJECT
#endif
namespace Media { namespace Media {
DECLARE_ENUM_CLASS(KnownField, unsigned int); DECLARE_ENUM_CLASS(KnownField, unsigned int);
@ -12,7 +16,10 @@ DECLARE_ENUM_CLASS(KnownField, unsigned int);
namespace Settings { namespace Settings {
class KnownFieldModel : public Models::ChecklistModel class KnownFieldModel
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
: public Models::ChecklistModel
#endif
{ {
Q_OBJECT Q_OBJECT
public: public:
@ -23,6 +30,8 @@ public:
}; };
static const char *fieldName(Media::KnownField field); static const char *fieldName(Media::KnownField field);
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
static QString translatedFieldName(Media::KnownField field); static QString translatedFieldName(Media::KnownField field);
static Models::ChecklistItem mkItem(Media::KnownField field, Qt::CheckState checkState = Qt::Checked); static Models::ChecklistItem mkItem(Media::KnownField field, Qt::CheckState checkState = Qt::Checked);
@ -31,12 +40,15 @@ public:
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
virtual QString labelForId(const QVariant &id) const; virtual QString labelForId(const QVariant &id) const;
#endif
}; };
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
inline Models::ChecklistItem KnownFieldModel::mkItem(Media::KnownField field, Qt::CheckState checkState) inline Models::ChecklistItem KnownFieldModel::mkItem(Media::KnownField field, Qt::CheckState checkState)
{ {
return Models::ChecklistItem(static_cast<int>(field), translatedFieldName(field), checkState); return Models::ChecklistItem(static_cast<int>(field), translatedFieldName(field), checkState);
} }
#endif
} }

View File

@ -1,9 +1,9 @@
#include "./main.h" #include "./main.h"
#include "./knownfieldmodel.h"
#include "../cli/mainfeatures.h" #include "../cli/mainfeatures.h"
#if defined(GUI_QTWIDGETS) #if defined(GUI_QTWIDGETS)
# include "../gui/initiate.h" # include "../gui/initiate.h"
# include "./knownfieldmodel.h"
#elif defined(GUI_QTQUICK) #elif defined(GUI_QTQUICK)
#endif #endif

View File

@ -1,8 +1,10 @@
#include "./mainfeatures.h" #include "./mainfeatures.h"
#include "../application/knownfieldmodel.h" #include "../application/knownfieldmodel.h"
#include "../misc/utility.h" #if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
#include "../misc/htmlinfo.h" # include "../misc/utility.h"
# include "../misc/htmlinfo.h"
#endif
#include <tagparser/mediafileinfo.h> #include <tagparser/mediafileinfo.h>
#include <tagparser/tag.h> #include <tagparser/tag.h>
@ -18,18 +20,24 @@
#include <c++utilities/io/catchiofailure.h> #include <c++utilities/io/catchiofailure.h>
#include <c++utilities/misc/memory.h> #include <c++utilities/misc/memory.h>
#include <QDir> #if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
# include <QDir>
#endif
#include <iostream> #include <iostream>
#include <cstring>
#include <algorithm>
using namespace std; using namespace std;
using namespace ApplicationUtilities; using namespace ApplicationUtilities;
using namespace ConversionUtilities; using namespace ConversionUtilities;
using namespace ChronoUtilities; using namespace ChronoUtilities;
using namespace EscapeCodes; using namespace EscapeCodes;
using namespace Utility;
using namespace Settings; using namespace Settings;
using namespace Media; using namespace Media;
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
using namespace Utility;
#endif
namespace Cli { namespace Cli {
@ -57,7 +65,7 @@ struct FieldDenotation
DenotationType type; DenotationType type;
TagType tagType; TagType tagType;
TagTarget tagTarget; TagTarget tagTarget;
std::vector<std::pair<unsigned int, QString> > values; vector<pair<unsigned int, string> > values;
}; };
FieldDenotation::FieldDenotation(KnownField field) : FieldDenotation::FieldDenotation(KnownField field) :
@ -71,23 +79,23 @@ inline bool isDigit(char c)
return c >= '0' && c <= '9'; return c >= '0' && c <= '9';
} }
QString incremented(const QString &str, unsigned int toIncrement = 1) string incremented(const string &str, unsigned int toIncrement = 1)
{ {
QString res; string res;
res.reserve(str.size()); res.reserve(str.size());
unsigned int value = 0; unsigned int value = 0;
bool hasValue = false; bool hasValue = false;
for(const QChar &c : str) { for(const char &c : str) {
if(toIncrement && c.isDigit()) { if(toIncrement && c >= '0' && c <= '9') {
value = value * 10 + static_cast<unsigned int>(c.digitValue()); value = value * 10 + static_cast<unsigned int>(c - '0');
hasValue = true; hasValue = true;
} else { } else {
if(hasValue) { if(hasValue) {
res.append(QString::number(value + 1)); res += numberToString(value + 1);
hasValue = false; hasValue = false;
--toIncrement; --toIncrement;
} }
res.append(c); res += c;
} }
} }
return res; return res;
@ -422,7 +430,7 @@ vector<FieldDenotation> parseFieldDenotations(const Argument &fieldsArg, bool re
if(readOnly) { if(readOnly) {
cerr << "Warning: Specified value for \"" << string(fieldDenotationString, fieldNameLen) << "\" will be ignored." << endl; cerr << "Warning: Specified value for \"" << string(fieldDenotationString, fieldNameLen) << "\" will be ignored." << endl;
} else { } else {
fieldDenotation.values.emplace_back(make_pair(mult == 1 ? fieldDenotation.values.size() : fileIndex, QString::fromLocal8Bit(equationPos + 1))); fieldDenotation.values.emplace_back(make_pair(mult == 1 ? fieldDenotation.values.size() : fileIndex, (equationPos + 1)));
} }
} }
} }
@ -567,6 +575,7 @@ bool AttachmentInfo::next(AbstractContainer *container)
void generateFileInfo(const ArgumentOccurance &, const Argument &inputFileArg, const Argument &outputFileArg, const Argument &validateArg) void generateFileInfo(const ArgumentOccurance &, const Argument &inputFileArg, const Argument &outputFileArg, const Argument &validateArg)
{ {
CMD_UTILS_START_CONSOLE; CMD_UTILS_START_CONSOLE;
#if defined(GUI_QTWIDGETS) || defined(GUI_QTQUICK)
try { try {
// parse tags // parse tags
MediaFileInfo inputFileInfo(inputFileArg.values().front()); MediaFileInfo inputFileInfo(inputFileArg.values().front());
@ -587,6 +596,12 @@ void generateFileInfo(const ArgumentOccurance &, const Argument &inputFileArg, c
::IoUtilities::catchIoFailure(); ::IoUtilities::catchIoFailure();
cerr << "Error: An IO failure occured when reading the file \"" << inputFileArg.values().front() << "\"." << endl; cerr << "Error: An IO failure occured when reading the file \"" << inputFileArg.values().front() << "\"." << endl;
} }
#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
} }
void printProperty(const char *propName, const char *value, const char *suffix = nullptr, size_t intention = 4) void printProperty(const char *propName, const char *value, const char *suffix = nullptr, size_t intention = 4)
@ -804,11 +819,11 @@ void displayTagInfo(const Argument &fieldsArg, const Argument &filesArg, const A
} }
// write value // write value
try { try {
const auto textValue = tagValueToQString(value); const auto textValue = value.toString(TagTextEncoding::Utf8);
if(textValue.isEmpty()) { if(textValue.empty()) {
cout << "can't display here (see --extract)"; cout << "can't display here (see --extract)";
} else { } else {
cout << textValue.toLocal8Bit().data(); cout << textValue;
} }
} catch(const ConversionException &) { } catch(const ConversionException &) {
cout << "conversion error"; cout << "conversion error";
@ -832,11 +847,11 @@ void displayTagInfo(const Argument &fieldsArg, const Argument &filesArg, const A
cout << "none"; cout << "none";
} else { } else {
try { try {
const auto textValue = tagValueToQString(value); const auto textValue = value.toString(TagTextEncoding::Utf8);
if(textValue.isEmpty()) { if(textValue.empty()) {
cout << "can't display here (see --extract)"; cout << "can't display here (see --extract)";
} else { } else {
cout << textValue.toLocal8Bit().data(); cout << textValue;
} }
} catch(const ConversionException &) { } catch(const ConversionException &) {
cout << "conversion error"; cout << "conversion error";
@ -979,7 +994,7 @@ void setTagInfo(const SetTagInfoArgs &args)
if((fieldDenotation.tagType == TagType::Unspecified if((fieldDenotation.tagType == TagType::Unspecified
|| (fieldDenotation.tagType | tagType) != TagType::Unspecified) || (fieldDenotation.tagType | tagType) != TagType::Unspecified)
&& (!targetSupported || fieldDenotation.tagTarget == tagTarget)) { && (!targetSupported || fieldDenotation.tagTarget == tagTarget)) {
pair<unsigned int, QString> *selectedDenotatedValue = nullptr; pair<unsigned int, string> *selectedDenotatedValue = nullptr;
for(auto &someDenotatedValue : fieldDenotation.values) { for(auto &someDenotatedValue : fieldDenotation.values) {
if(someDenotatedValue.first <= fileIndex) { if(someDenotatedValue.first <= fileIndex) {
if(!selectedDenotatedValue || (someDenotatedValue.first > selectedDenotatedValue->first)) { if(!selectedDenotatedValue || (someDenotatedValue.first > selectedDenotatedValue->first)) {
@ -989,11 +1004,11 @@ void setTagInfo(const SetTagInfoArgs &args)
} }
if(selectedDenotatedValue) { if(selectedDenotatedValue) {
if(fieldDenotation.type == DenotationType::File) { if(fieldDenotation.type == DenotationType::File) {
if(selectedDenotatedValue->second.isEmpty()) { if(selectedDenotatedValue->second.empty()) {
tag->setValue(fieldDenotation.field, TagValue()); tag->setValue(fieldDenotation.field, TagValue());
} else { } else {
try { try {
MediaFileInfo fileInfo(selectedDenotatedValue->second.toLocal8Bit().constData()); MediaFileInfo fileInfo(selectedDenotatedValue->second);
fileInfo.open(true); fileInfo.open(true);
fileInfo.parseContainerFormat(); fileInfo.parseContainerFormat();
auto buff = make_unique<char []>(fileInfo.size()); auto buff = make_unique<char []>(fileInfo.size());
@ -1014,7 +1029,7 @@ void setTagInfo(const SetTagInfoArgs &args)
if(!tag->canEncodingBeUsed(denotedEncoding)) { if(!tag->canEncodingBeUsed(denotedEncoding)) {
usedEncoding = tag->proposedTextEncoding(); usedEncoding = tag->proposedTextEncoding();
} }
tag->setValue(fieldDenotation.field, qstringToTagValue(selectedDenotatedValue->second, usedEncoding)); tag->setValue(fieldDenotation.field, TagValue(selectedDenotatedValue->second, TagTextEncoding::Utf8, usedEncoding));
if(fieldDenotation.type == DenotationType::Increment && tag == tags.back()) { if(fieldDenotation.type == DenotationType::Increment && tag == tags.back()) {
selectedDenotatedValue->second = incremented(selectedDenotatedValue->second); selectedDenotatedValue->second = incremented(selectedDenotatedValue->second);
} }

View File

@ -10,7 +10,7 @@
#include <qtutilities/settingsdialog/optioncategory.h> #include <qtutilities/settingsdialog/optioncategory.h>
#include <qtutilities/settingsdialog/optioncategorymodel.h> #include <qtutilities/settingsdialog/optioncategorymodel.h>
//#include <qtutilities/settingsdialog/qtsettings.h> #include <qtutilities/settingsdialog/qtsettings.h>
#include <QFileDialog> #include <QFileDialog>
@ -603,7 +603,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) :
category->assignPages(QList<Dialogs::OptionPage *>() << new FileBrowserGeneralOptionPage); category->assignPages(QList<Dialogs::OptionPage *>() << new FileBrowserGeneralOptionPage);
categories << category; categories << category;
//categories << Dialogs::qtOptionCategory(this); categories << Dialogs::qtOptionCategory(this);
categoryModel()->setCategories(categories); categoryModel()->setCategories(categories);