/**
* \file clicommand.cpp
* Command line interface commands.
*
* \b Project: Kid3
* \author Urs Fleisch
* \date 11 Aug 2013
*
* Copyright (C) 2013-2018 Urs Fleisch
*
* This file is part of Kid3.
*
* Kid3 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Kid3 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "clicommand.h"
#include
#include
#include
#include
#include
#include
#include
#include "kid3cli.h"
#include "kid3application.h"
#include "fileproxymodel.h"
#include "frametablemodel.h"
#include "filefilter.h"
#include "importconfig.h"
#include "exportconfig.h"
#include "filterconfig.h"
#include "fileconfig.h"
#include "rendirconfig.h"
#include "batchimportconfig.h"
#include "formatconfig.h"
#include "networkconfig.h"
#include "numbertracksconfig.h"
#include "playlistconfig.h"
#include "tagconfig.h"
#include "batchimporter.h"
#include "downloadclient.h"
#include "dirrenamer.h"
namespace {
/** Default command timeout in milliseconds. */
const int DEFAULT_TIMEOUT_MS = 3000;
/**
* Available names for groups in config command.
* If this list is modified, adapt also the cfgFuncs in getConfig().
*/
const QStringList configNames{
QLatin1String("BatchImport"),
QLatin1String("Export"),
QLatin1String("File"),
QLatin1String("FilenameFormat"),
QLatin1String("Filter"),
QLatin1String("Import"),
QLatin1String("Network"),
QLatin1String("NumberTracks"),
QLatin1String("Playlist"),
QLatin1String("RenameFolder"),
QLatin1String("Tag"),
QLatin1String("TagFormat")
};
/** Properties which shall not be displayed as config options. */
const QStringList excludedConfigPropertyNames{
QLatin1String("objectName"),
QLatin1String("windowGeometry"),
QLatin1String("exportWindowGeometry"),
QLatin1String("importServer"),
QLatin1String("importVisibleColumns"),
QLatin1String("importWindowGeometry"),
QLatin1String("browseCoverArtWindowGeometry"),
QLatin1String("quickAccessFrames"),
QLatin1String("quickAccessFrameOrder"),
QLatin1String("taggedFileFeatures")
};
/**
* Get a configuration object for a given group name.
* @param name group name
* @return QObject with configuration options as properties.
*/
GeneralConfig* getConfig(const QString& name)
{
int idx = configNames.indexOf(name);
if (idx == -1) {
return nullptr;
}
// Change this list together with configNames.
static const std::function cfgFuncs[] = {
[]() { return &BatchImportConfig::instance(); },
[]() { return &ExportConfig::instance(); },
[]() { return &FileConfig::instance(); },
[]() { return &FilenameFormatConfig::instance(); },
[]() { return &FilterConfig::instance(); },
[]() { return &ImportConfig::instance(); },
[]() { return &NetworkConfig::instance(); },
[]() { return &NumberTracksConfig::instance(); },
[]() { return &PlaylistConfig::instance(); },
[]() { return &RenDirConfig::instance(); },
[]() { return &TagConfig::instance(); },
[]() { return &TagFormatConfig::instance(); }
};
return cfgFuncs[idx]();
}
/**
* Convert an integer value to the corresponding enum name string.
* @param group config group
* @param option config option
* @param value enum value as integer
* @return enum value as string, original int value if invalid.
*/
QVariant configIntToEnumName(const QString& group, const QString& option,
const QVariant& value)
{
const int enumVal = value.toInt();
if (option == QLatin1String("importDest") ||
option == QLatin1String("exportSource") ||
option == QLatin1String("numberTracksDestination")) {
QString tagMaskStr;
for (Frame::TagNumber tagNr :
Frame::tagNumbersFromMask(Frame::tagVersionCast(enumVal))) {
tagMaskStr += Frame::tagNumberToString(tagNr);
}
return tagMaskStr;
} else if (option == QLatin1String("caseConversion")) {
const QMetaObject metaObj = FormatConfig::staticMetaObject;
const char* key = metaObj.enumerator(
metaObj.indexOfEnumerator("CaseConversion")).valueToKey(enumVal);
if (key) {
return QString::fromLatin1(key);
}
} else if (group == QLatin1String("Playlist") &&
option == QLatin1String("location")) {
const QMetaObject metaObj = PlaylistConfig::staticMetaObject;
const char* key = metaObj.enumerator(
metaObj.indexOfEnumerator("PlaylistLocation")).valueToKey(enumVal);
if (key) {
return QString::fromLatin1(key);
}
} else if (group == QLatin1String("Playlist") &&
option == QLatin1String("format")) {
const QMetaObject metaObj = PlaylistConfig::staticMetaObject;
const char* key = metaObj.enumerator(
metaObj.indexOfEnumerator("PlaylistFormat")).valueToKey(enumVal);
if (key) {
return QString::fromLatin1(key);
}
} else if (group == QLatin1String("Tag") &&
option == QLatin1String("id3v2Version")) {
const QMetaObject metaObj = TagConfig::staticMetaObject;
const char* key = metaObj.enumerator(
metaObj.indexOfEnumerator("Id3v2Version")).valueToKey(enumVal);
if (key) {
return QString::fromLatin1(key);
}
} else if (group == QLatin1String("Tag") &&
option == QLatin1String("textEncoding")) {
const QMetaObject metaObj = TagConfig::staticMetaObject;
const char* key = metaObj.enumerator(
metaObj.indexOfEnumerator("TextEncoding")).valueToKey(enumVal);
if (key) {
return QString::fromLatin1(key);
}
}
return value;
}
/**
* Convert an enum value name to the corresponding integer value.
* @param group config group
* @param option config option
* @param enumName enum value as string
* @return enum value as integer, original string value if invalid.
*/
QVariant configIntFromEnumName(const QString& group, const QString& option,
const QVariant& value)
{
const QString enumName = value.toString();
int val;
bool ok;
if (option == QLatin1String("importDest") ||
option == QLatin1String("exportSource") ||
option == QLatin1String("numberTracksDestination")) {
val = 0;
if (!enumName.isEmpty() && enumName.at(0).isDigit()) {
FOR_ALL_TAGS(tagNr) {
if (enumName.contains(Frame::tagNumberToString(tagNr))) {
val |= Frame::tagVersionFromNumber(tagNr);
}
}
if (val != 0) {
return val;
}
}
} else if (option == QLatin1String("caseConversion")) {
const QMetaObject metaObj = FormatConfig::staticMetaObject;
val = metaObj.enumerator(metaObj.indexOfEnumerator("CaseConversion"))
.keyToValue(enumName.toLatin1(), &ok);
if (ok) {
return val;
}
} else if (group == QLatin1String("Playlist") &&
option == QLatin1String("location")) {
const QMetaObject metaObj = PlaylistConfig::staticMetaObject;
val = metaObj.enumerator(metaObj.indexOfEnumerator("PlaylistLocation"))
.keyToValue(enumName.toLatin1(), &ok);
if (ok) {
return val;
}
} else if (group == QLatin1String("Playlist") &&
option == QLatin1String("format")) {
const QMetaObject metaObj = PlaylistConfig::staticMetaObject;
val = metaObj.enumerator(metaObj.indexOfEnumerator("PlaylistFormat"))
.keyToValue(enumName.toLatin1(), &ok);
if (ok) {
return val;
}
} else if (group == QLatin1String("Tag") &&
option == QLatin1String("id3v2Version")) {
const QMetaObject metaObj = TagConfig::staticMetaObject;
val = metaObj.enumerator(metaObj.indexOfEnumerator("Id3v2Version"))
.keyToValue(enumName.toLatin1(), &ok);
if (ok) {
return val;
}
} else if (group == QLatin1String("Tag") &&
option == QLatin1String("textEncoding")) {
const QMetaObject metaObj = TagConfig::staticMetaObject;
val = metaObj.enumerator(metaObj.indexOfEnumerator("TextEncoding"))
.keyToValue(enumName.toLatin1(), &ok);
if (ok) {
return val;
}
}
val = enumName.toInt(&ok);
if (ok) {
return val;
}
return QVariant();
}
}
/**
* Constructor.
* @param processor command line processor
* @param name name with which command is invoked
* @param help help text for command
* @param argspec argument specification
*/
CliCommand::CliCommand(Kid3Cli* processor,
const QString& name, const QString& help,
const QString& argspec)
: QObject(processor), m_processor(processor), m_name(name), m_help(help),
m_argspec(argspec), m_timerId(0), m_timeoutMs(DEFAULT_TIMEOUT_MS), m_result(0)
{
}
/**
* Reset state to defaults.
*/
void CliCommand::clear()
{
if (m_timerId != 0) {
killTimer(m_timerId);
m_timerId = 0;
}
cli()->finishWriting();
m_errorMsg.clear();
m_args.clear();
m_result = 0;
}
/**
* Execute command.
*/
void CliCommand::execute()
{
if (m_timerId != 0) {
killTimer(m_timerId);
m_timerId = 0;
}
int msec = m_processor->getTimeout();
if (msec == 0) {
msec = getTimeout();
}
if (msec > 0) {
m_timerId = startTimer(msec);
}
connectResultSignal();
startCommand();
}
/**
* Terminate command.
*/
void CliCommand::terminate() {
if (m_timerId != 0) {
killTimer(m_timerId);
m_timerId = 0;
}
disconnectResultSignal();
emit finished();
}
/**
* Connect signals used to emit finished().
* This method is called after startCommand(). The default implementation
* invokes terminate() in the event loop. It can be overridden to connect
* signals connected to terminate() to signal termination of the command.
*/
void CliCommand::connectResultSignal()
{
QTimer::singleShot(0, this, &CliCommand::terminate);
}
/**
* Disconnect signals used to emit finished().
* This method is called from terminate(). The default implementation
* does nothing. It can be overridden to disconnect signals connected
* in connectResultSignal().
*/
void CliCommand::disconnectResultSignal()
{
}
/**
* Called on timeout.
* @param event timer event
*/
void CliCommand::timerEvent(QTimerEvent*) {
setError(tr("Timeout"));
terminate();
}
/**
* Get parameter for task mask.
* @param nr index in args()
* @param useDefault if true use cli()->tagMask() if no parameter found
* @return tag versions.
*/
Frame::TagVersion CliCommand::getTagMaskParameter(int nr,
bool useDefault) const
{
int tagMask = 0;
if (m_args.size() > nr) {
const QString& tagStr = m_args.at(nr);
if (!tagStr.isEmpty() && tagStr.at(0).isDigit()) {
FOR_ALL_TAGS(tagNr) {
if (tagStr.contains(Frame::tagNumberToString(tagNr))) {
tagMask |= Frame::tagVersionFromNumber(tagNr);
}
}
if (tagMask == 0)
tagMask = tagStr.toInt();
}
}
if (tagMask == 0 && useDefault) {
tagMask = m_processor->tagMask();
}
return Frame::tagVersionCast(tagMask);
}
/**
* Show usage of command.
*/
void CliCommand::showUsage()
{
cli()->writeHelp(name(), true);
setError(QLatin1String("_Usage"));
}
HelpCommand::HelpCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("help"), tr("Help"),
QLatin1String("[S]\nS = ") + tr("Command name"))
{
}
void HelpCommand::startCommand()
{
cli()->writeHelp(args().size() > 1 ? args().at(1) : QString());
}
TimeoutCommand::TimeoutCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("timeout"), tr("Overwrite timeout"),
QLatin1String("[S]\nS = \"default\" | \"off\" | ") + tr("Time") +
QLatin1String(" [ms]"))
{
}
void TimeoutCommand::startCommand()
{
int cliTimeout = cli()->getTimeout();
if (args().size() > 1) {
const QString& val = args().at(1);
if (val == QLatin1String("off")) {
cliTimeout = -1;
} else if (val == QLatin1String("default")) {
cliTimeout = 0;
} else {
QString msStr = val;
if (msStr.endsWith(QLatin1String("ms"))) {
msStr.truncate(msStr.length() - 2);
}
bool ok;
int ms = msStr.toInt(&ok);
if (ok && ms > 0) {
cliTimeout = ms;
}
}
cli()->setTimeout(cliTimeout);
}
QString value;
if (cliTimeout < 0) {
value = QLatin1String("off");
} else if (cliTimeout == 0) {
value = QLatin1String("default");
} else {
value = QString::number(cliTimeout);
value += QLatin1String(" ms");
}
cli()->writeResult(QVariantMap{{QLatin1String("timeout"), value}});
}
QuitCommand::QuitCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("exit"), tr("Quit application"),
QLatin1String("[S]\nS = \"force\""))
{
}
void QuitCommand::startCommand()
{
if (cli()->app()->isModified() && !cli()->app()->getDirName().isEmpty()) {
if (!(args().size() > 1 && args().at(1) == QLatin1String("force"))) {
cli()->writeResult(tr("The current folder has been modified.") %
QLatin1Char('\n') %
tr("Type 'exit force' to quit."));
terminate();
return;
}
}
disconnect(this, &CliCommand::finished, cli(), &Kid3Cli::onCommandFinished);
cli()->terminate();
}
void QuitCommand::connectResultSignal()
{
// Do not signal finished() to avoid printing prompt.
}
CdCommand::CdCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("cd"), tr("Change folder"),
QLatin1String("[P]"))
{
}
void CdCommand::startCommand()
{
QStringList paths;
if (args().size() > 1) {
paths = args().mid(1);
} else {
paths.append(QDir::homePath());
}
if (!cli()->openDirectory(Kid3Cli::expandWildcards(paths))) {
setError(tr("%1 does not exist").arg(paths.join(QLatin1String(", "))));
terminate();
}
}
void CdCommand::connectResultSignal()
{
connect(cli()->app(), &Kid3Application::directoryOpened,
this, &CdCommand::terminate);
}
void CdCommand::disconnectResultSignal()
{
disconnect(cli()->app(), &Kid3Application::directoryOpened,
this, &CdCommand::terminate);
}
PwdCommand::PwdCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("pwd"),
tr("Print the filename of the current folder"))
{
}
void PwdCommand::startCommand()
{
QString path = cli()->app()->getDirPath();
if (path.isNull()) {
path = QDir::currentPath();
cli()->app()->openDirectory({path});
}
cli()->writeResult(path);
}
LsCommand::LsCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("ls"), tr("Folder list"))
{
setTimeout(10000);
}
void LsCommand::startCommand()
{
cli()->writeFileList();
}
SaveCommand::SaveCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("save"), tr("Saves the changed files"))
{
}
void SaveCommand::startCommand()
{
QStringList errorFiles = cli()->app()->saveDirectory();
if (errorFiles.isEmpty()) {
cli()->updateSelection();
} else {
setError(tr("Error while writing file:\n") +
errorFiles.join(QLatin1String("\n")));
}
}
SelectCommand::SelectCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("select"), tr("Select file"),
QLatin1String("[P|S]\n"
"S = \"all\" | \"none\" | \"first\" | \"previous\" | \"next\""))
{
}
void SelectCommand::startCommand()
{
if (args().size() > 1) {
const QString& param = args().at(1);
if (param == QLatin1String("all")) {
cli()->app()->selectAllFiles();
} else if (param == QLatin1String("none")) {
cli()->app()->deselectAllFiles();
} else if (param == QLatin1String("first")) {
setResult(cli()->app()->firstFile(true) ? 0 : 1);
} else if (param == QLatin1String("previous")) {
setResult(cli()->app()->previousFile(true) ? 0 : 1);
} else if (param == QLatin1String("next")) {
setResult(cli()->app()->nextFile(true) ? 0 : 1);
} else {
QStringList paths = args().mid(1);
if (!cli()->selectFile(Kid3Cli::expandWildcards(paths))) {
setError(tr("%1 not found").arg(paths.join(QLatin1String(", "))));
}
}
} else {
cli()->updateSelection();
}
}
TagCommand::TagCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("tag"), tr("Select tag"),
QLatin1String("[T]"))
{
}
void TagCommand::startCommand()
{
Frame::TagVersion tagMask = getTagMaskParameter(1, false);
if (tagMask != Frame::TagNone) {
cli()->setTagMask(tagMask);
} else {
cli()->writeTagMask();
}
}
GetCommand::GetCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("get"), tr("Get tag frame"),
QLatin1String("[N|S] [T]\nS = \"all\""))
{
}
void GetCommand::startCommand()
{
int numArgs = args().size();
QString name = numArgs > 1
? Frame::getNameForTranslatedFrameName(args().at(1))
: QLatin1String("all");
Frame::TagVersion tagMask = getTagMaskParameter(2);
if (name == QLatin1String("all")) {
cli()->writeFileInformation(tagMask);
} else {
for (Frame::TagNumber tagNr : Frame::tagNumbersFromMask(tagMask)) {
QString value = cli()->app()->getFrame(Frame::tagVersionFromNumber(tagNr),
name);
if (!(tagNr == Frame::Tag_1 ? value.isEmpty() : value.isNull())) {
cli()->writeResult(value);
break;
}
}
}
}
SetCommand::SetCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("set"), tr("Set tag frame"),
QLatin1String("N V [T]"))
{
}
void SetCommand::startCommand()
{
int numArgs = args().size();
if (numArgs > 2) {
QString name = Frame::getNameForTranslatedFrameName(args().at(1));
const QString& value = args().at(2);
Frame::TagVersion tagMask = getTagMaskParameter(3);
if (cli()->app()->setFrame(tagMask, name, value)) {
if (!name.endsWith(QLatin1String(".selected"))) {
cli()->updateSelectedFiles();
cli()->updateSelection();
}
} else if (!value.isEmpty()) {
setError(tr("Could not set \"%1\" for %2").arg(value, name));
}
} else {
showUsage();
}
}
RevertCommand::RevertCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("revert"),
tr("Revert"))
{
}
void RevertCommand::startCommand()
{
cli()->app()->revertFileModifications();
}
ImportCommand::ImportCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("import"),
tr("Import from file"),
QLatin1String("P S [T]\nP S = ") +
tr("File path") + QLatin1Char(' ') + tr("Format name") +
QLatin1String(" | tags ") + tr("Source") + QLatin1Char(' ') +
tr("Extraction"))
{
}
void ImportCommand::startCommand()
{
int numArgs = args().size();
if (numArgs > 3 && args().at(1).startsWith(QLatin1String("tags"))) {
const QString& source = args().at(2);
const QString& extraction = args().at(3);
Frame::TagVersion tagMask = getTagMaskParameter(4);
if (args().at(1).contains(QLatin1String("sel"))) {
QStringList returnValues =
cli()->app()->importFromTagsToSelection(tagMask, source, extraction);
if (!returnValues.isEmpty()) {
cli()->writeResult(returnValues);
}
} else {
cli()->app()->importFromTags(tagMask, source, extraction);
}
} else if (numArgs > 2) {
const QString& path = args().at(1);
const QString& fmtName = args().at(2);
bool ok;
int fmtIdx = fmtName.toInt(&ok);
if (!ok) {
fmtIdx = ImportConfig::instance().importFormatNames().indexOf(fmtName);
if (fmtIdx == -1) {
QString errMsg = tr("%1 not found.").arg(fmtName);
errMsg += QLatin1Char('\n');
errMsg += tr("Available");
errMsg += QLatin1String(": ");
errMsg += ImportConfig::instance().importFormatNames().join(
QLatin1String(", "));
errMsg += QLatin1Char('.');
setError(errMsg);
return;
}
}
Frame::TagVersion tagMask = getTagMaskParameter(3);
if (!cli()->app()->importTags(tagMask, path, fmtIdx)) {
setError(tr("Error"));
}
} else {
showUsage();
}
}
BatchImportCommand::BatchImportCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("autoimport"),
tr("Automatic import"), QLatin1String("[S] [T]\nS = ") +
tr("Profile name"))
{
setTimeout(60000);
}
void BatchImportCommand::startCommand()
{
int numArgs = args().size();
const QString& profileName = numArgs > 1
? args().at(1) : QLatin1String("All");
Frame::TagVersion tagMask = getTagMaskParameter(2);
if (!cli()->app()->batchImport(profileName, tagMask)) {
QString errMsg = tr("%1 not found.").arg(profileName);
errMsg += QLatin1Char('\n');
errMsg += tr("Available");
errMsg += QLatin1String(": ");
errMsg += BatchImportConfig::instance().profileNames().join(
QLatin1String(", "));
errMsg += QLatin1Char('.');
setError(errMsg);
terminate();
}
}
void BatchImportCommand::connectResultSignal()
{
BatchImporter* importer = cli()->app()->getBatchImporter();
connect(importer, &BatchImporter::reportImportEvent,
this, &BatchImportCommand::onReportImportEvent);
connect(importer, &BatchImporter::finished,
this, &BatchImportCommand::terminate);
}
void BatchImportCommand::disconnectResultSignal()
{
BatchImporter* importer = cli()->app()->getBatchImporter();
disconnect(importer, &BatchImporter::reportImportEvent,
this, &BatchImportCommand::onReportImportEvent);
disconnect(importer, &BatchImporter::finished,
this, &BatchImportCommand::terminate);
}
void BatchImportCommand::onReportImportEvent(int type, const QString& text)
{
QString typeStr;
switch (type) {
case BatchImporter::ReadingDirectory:
typeStr = QLatin1String("readingDirectory");
break;
case BatchImporter::Started:
typeStr = QLatin1String("started");
break;
case BatchImporter::SourceSelected:
typeStr = QLatin1String("source");
break;
case BatchImporter::QueryingAlbumList:
typeStr = QLatin1String("querying");
break;
case BatchImporter::FetchingTrackList:
case BatchImporter::FetchingCoverArt:
typeStr = QLatin1String("fetching");
break;
case BatchImporter::TrackListReceived:
typeStr = QLatin1String("data received");
break;
case BatchImporter::CoverArtReceived:
typeStr = QLatin1String("cover");
break;
case BatchImporter::Finished:
typeStr = QLatin1String("finished");
break;
case BatchImporter::Aborted:
typeStr = QLatin1String("aborted");
break;
case BatchImporter::Error:
typeStr = QLatin1String("error");
}
QVariantMap event{{QLatin1String("type"), typeStr}};
if (!text.isEmpty()) {
event.insert(QLatin1String("data"), text);
}
cli()->writeResult(QVariantMap{{QLatin1String("event"), event}});
}
AlbumArtCommand::AlbumArtCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("albumart"),
tr("Download album cover artwork"),
QLatin1String("U [S]\nS = \"all\""))
{
setTimeout(10000);
}
void AlbumArtCommand::startCommand()
{
int numArgs = args().size();
if (numArgs > 1) {
const QString& url = args().at(1);
cli()->app()->downloadImage(url,
numArgs > 2 && args().at(2) == QLatin1String("all"));
} else {
showUsage();
terminate();
}
}
void AlbumArtCommand::connectResultSignal()
{
DownloadClient* downloadClient = cli()->app()->getDownloadClient();
connect(downloadClient, &DownloadClient::downloadFinished,
this, &AlbumArtCommand::onDownloadFinished);
}
void AlbumArtCommand::disconnectResultSignal()
{
DownloadClient* downloadClient = cli()->app()->getDownloadClient();
disconnect(downloadClient,
&DownloadClient::downloadFinished,
this, &AlbumArtCommand::onDownloadFinished);
}
void AlbumArtCommand::onDownloadFinished(
const QByteArray& data, const QString& mimeType, const QString& url)
{
cli()->app()->imageDownloaded(data, mimeType, url);
terminate();
}
ExportCommand::ExportCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("export"),
tr("Export to file"),
QLatin1String("P S [T]\nS = ") + tr("Format name"))
{
}
void ExportCommand::startCommand()
{
int numArgs = args().size();
if (numArgs > 2) {
const QString& path = args().at(1);
const QString& fmtName = args().at(2);
bool ok;
int fmtIdx = fmtName.toInt(&ok);
if (!ok) {
fmtIdx = ExportConfig::instance().exportFormatNames().indexOf(fmtName);
if (fmtIdx == -1) {
QString errMsg = tr("%1 not found.").arg(fmtName);
errMsg += QLatin1Char('\n');
errMsg += tr("Available");
errMsg += QLatin1String(": ");
errMsg += ExportConfig::instance().exportFormatNames().join(
QLatin1String(", "));
errMsg += QLatin1Char('.');
setError(errMsg);
return;
}
}
Frame::TagVersion tagMask = getTagMaskParameter(3);
if (!cli()->app()->exportTags(tagMask, path, fmtIdx)) {
setError(tr("Error"));
}
} else {
showUsage();
}
}
PlaylistCommand::PlaylistCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("playlist"), tr("Create playlist"))
{
}
void PlaylistCommand::startCommand()
{
if (!cli()->app()->writePlaylist()) {
setError(tr("Error"));
}
}
FilenameFormatCommand::FilenameFormatCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("filenameformat"),
tr("Apply filename format"))
{
}
void FilenameFormatCommand::startCommand()
{
cli()->app()->applyFilenameFormat();
}
TagFormatCommand::TagFormatCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("tagformat"), tr("Apply tag format"))
{
}
void TagFormatCommand::startCommand()
{
cli()->app()->applyTagFormat();
}
TextEncodingCommand::TextEncodingCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("textencoding"),
tr("Apply text encoding"))
{
}
void TextEncodingCommand::startCommand()
{
cli()->app()->applyTextEncoding();
}
RenameDirectoryCommand::RenameDirectoryCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("renamedir"), tr("Rename folder"),
QLatin1String("[F] [S] [T]\nS = \"create\" | \"rename\" | \"dryrun\"")),
m_dryRun(false)
{
}
void RenameDirectoryCommand::startCommand()
{
Frame::TagVersion tagMask = Frame::TagNone;
QString format;
bool create = false;
m_dryRun = false;
for (int i = 1; i < args().size(); ++i) {
bool ok = false;
if (tagMask == Frame::TagNone) {
tagMask = getTagMaskParameter(i, false);
ok = tagMask != Frame::TagNone;
}
if (!ok) {
const QString& param = args().at(i);
if (param == QLatin1String("create")) {
create = true;
} else if (param == QLatin1String("rename")) {
create = false;
} else if (param == QLatin1String("dryrun")) {
m_dryRun = true;
} else if (format.isEmpty()) {
format = param;
}
}
}
if (tagMask == Frame::TagNone) {
tagMask = cli()->tagMask();
}
if (format.isEmpty()) {
format = RenDirConfig::instance().dirFormat();
}
if (!cli()->app()->renameDirectory(tagMask, format, create)) {
terminate();
}
}
void RenameDirectoryCommand::connectResultSignal()
{
DirRenamer* renamer = cli()->app()->getDirRenamer();
connect(renamer, &DirRenamer::actionScheduled,
this, &RenameDirectoryCommand::onActionScheduled);
connect(cli()->app(), &Kid3Application::renameActionsScheduled,
this, &RenameDirectoryCommand::onRenameActionsScheduled);
}
void RenameDirectoryCommand::disconnectResultSignal()
{
DirRenamer* renamer = cli()->app()->getDirRenamer();
disconnect(renamer, &DirRenamer::actionScheduled,
this, &RenameDirectoryCommand::onActionScheduled);
disconnect(cli()->app(), &Kid3Application::renameActionsScheduled,
this, &RenameDirectoryCommand::onRenameActionsScheduled);
}
void RenameDirectoryCommand::onActionScheduled(const QStringList& actionStrs)
{
QVariantMap event{{QLatin1String("type"), actionStrs.at(0)}};
QVariantMap data;
if (actionStrs.size() > 1) {
data.insert(QLatin1String("source"), actionStrs.at(1));
}
if (actionStrs.size() > 2) {
data.insert(QLatin1String("destination"), actionStrs.at(2));
}
if (!data.isEmpty()) {
event.insert(QLatin1String("data"), data);
}
cli()->writeResult(QVariantMap{{QLatin1String("event"), event}});
}
void RenameDirectoryCommand::onRenameActionsScheduled()
{
if (!m_dryRun) {
QString errMsg = cli()->app()->performRenameActions();
if (errMsg.isEmpty()) {
cli()->app()->deselectAllFiles();
} else {
setError(errMsg);
}
}
terminate();
}
NumberTracksCommand::NumberTracksCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("numbertracks"), tr("Number tracks"),
QLatin1String("[S] [T]\nS = ") + tr("Track number"))
{
}
void NumberTracksCommand::startCommand()
{
int numArgs = args().size();
int firstTrackNr = 1;
bool ok = false;
if (numArgs > 1) {
firstTrackNr = args().at(1).toInt(&ok);
}
if (!ok) {
firstTrackNr = 1;
}
Frame::TagVersion tagMask = getTagMaskParameter(2);
Kid3Application::NumberTrackOptions options;
options |= Kid3Application::NumberTracksEnabled;
options |= Kid3Application::NumberTracksResetCounterForEachDirectory;
cli()->app()->numberTracks(firstTrackNr, 0, tagMask, options);
}
FilterCommand::FilterCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("filter"), tr("Filter"),
QLatin1String("F|S\nS = ") + tr("Filter name"))
{
setTimeout(60000);
}
void FilterCommand::startCommand()
{
if (args().size() > 1) {
QString expression = args().at(1);
int fltIdx = FilterConfig::instance().filterNames().indexOf(expression);
if (fltIdx != -1) {
expression = FilterConfig::instance().filterExpressions().at(fltIdx);
} else if (!expression.isEmpty() &&
!expression.contains(QLatin1Char('%'))) {
// Probably an invalid expression
QString errMsg = tr("%1 not found.").arg(expression);
errMsg += QLatin1Char('\n');
errMsg += tr("Available");
errMsg += QLatin1String(": ");
errMsg += FilterConfig::instance().filterNames().join(
QLatin1String(", "));
errMsg += QLatin1Char('.');
setError(errMsg);
terminate();
return;
}
cli()->app()->applyFilter(expression);
} else {
showUsage();
terminate();
}
}
void FilterCommand::connectResultSignal()
{
connect(cli()->app(), &Kid3Application::fileFiltered,
this, &FilterCommand::onFileFiltered);
}
void FilterCommand::disconnectResultSignal()
{
cli()->app()->abortFilter();
disconnect(cli()->app(), &Kid3Application::fileFiltered,
this, &FilterCommand::onFileFiltered);
}
void FilterCommand::onFileFiltered(int type, const QString& fileName)
{
QString typeStr;
QString data;
bool finish = false;
switch (type) {
case FileFilter::Started:
typeStr = QLatin1String("started");
break;
case FileFilter::Directory:
typeStr = QLatin1String("filterEntered");
data = fileName;
break;
case FileFilter::ParseError:
typeStr = QLatin1String("parseError");
break;
case FileFilter::FilePassed:
typeStr = QLatin1String("filterPassed");
data = fileName;
break;
case FileFilter::FileFilteredOut:
typeStr = QLatin1String("filteredOut");
data = fileName;
break;
case FileFilter::Finished:
typeStr = QLatin1String("finished");
finish = true;
break;
case FileFilter::Aborted:
typeStr = QLatin1String("aborted");
finish = true;
break;
}
QVariantMap event{{QLatin1String("type"), typeStr}};
if (!data.isEmpty()) {
event.insert(QLatin1String("data"), data);
}
cli()->writeResult(QVariantMap{{QLatin1String("event"), event}});
if (finish) {
terminate();
}
}
ToId3v24Command::ToId3v24Command(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("to24"), tr("Convert ID3v2.3 to ID3v2.4"))
{
}
void ToId3v24Command::startCommand()
{
cli()->app()->convertToId3v24();
}
ToId3v23Command::ToId3v23Command(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("to23"), tr("Convert ID3v2.4 to ID3v2.3"))
{
}
void ToId3v23Command::startCommand()
{
cli()->app()->convertToId3v23();
}
TagToFilenameCommand::TagToFilenameCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("fromtag"), tr("Filename from tag"),
QLatin1String("[F] [T]"))
{
}
void TagToFilenameCommand::startCommand()
{
Frame::TagVersion tagMask = Frame::TagNone;
QString format;
for (int i = 1; i < qMin(args().size(), 3); ++i) {
bool ok = false;
if (tagMask == Frame::TagNone) {
tagMask = getTagMaskParameter(i, false);
ok = tagMask != Frame::TagNone;
}
if (!ok && format.isEmpty()) {
format = args().at(i);
}
}
if (tagMask == Frame::TagNone) {
tagMask = cli()->tagMask();
}
if (!format.isEmpty()) {
FileConfig::instance().setToFilenameFormat(format);
}
cli()->app()->getFilenameFromTags(tagMask);
}
FilenameToTagCommand::FilenameToTagCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("totag"), tr("Tag from filename"),
QLatin1String("[F] [T]"))
{
}
void FilenameToTagCommand::startCommand()
{
Frame::TagVersion tagMask = Frame::TagNone;
QString format;
for (int i = 1; i < qMin(args().size(), 3); ++i) {
bool ok = false;
if (tagMask == Frame::TagNone) {
tagMask = getTagMaskParameter(i, false);
ok = tagMask != Frame::TagNone;
}
if (!ok && format.isEmpty()) {
format = args().at(i);
}
}
if (tagMask == Frame::TagNone) {
tagMask = cli()->tagMask();
}
if (!format.isEmpty()) {
FileConfig::instance().setFromFilenameFormat(format);
}
cli()->app()->getTagsFromFilename(tagMask);
}
TagToOtherTagCommand::TagToOtherTagCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("syncto"), tr("Tag to other tag"),
QLatin1String("T"))
{
}
void TagToOtherTagCommand::startCommand()
{
Frame::TagVersion tagMask = getTagMaskParameter(1, false);
if (tagMask != Frame::TagNone) {
cli()->app()->copyToOtherTag(tagMask);
} else {
showUsage();
}
}
CopyCommand::CopyCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("copy"), tr("Copy"),
QLatin1String("[T]"))
{
}
void CopyCommand::startCommand()
{
Frame::TagVersion tagMask = getTagMaskParameter(1);
cli()->app()->copyTags(tagMask);
}
PasteCommand::PasteCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("paste"), tr("Paste"),
QLatin1String("[T]"))
{
}
void PasteCommand::startCommand()
{
Frame::TagVersion tagMask = getTagMaskParameter(1);
cli()->app()->pasteTags(tagMask);
}
RemoveCommand::RemoveCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("remove"), tr("Remove"),
QLatin1String("[T]"))
{
}
void RemoveCommand::startCommand()
{
Frame::TagVersion tagMask = getTagMaskParameter(1);
cli()->app()->removeTags(tagMask);
}
ConfigCommand::ConfigCommand(Kid3Cli* processor)
: CliCommand(processor, QLatin1String("config"), tr("Configure Kid3"),
QLatin1String("[S]\nS = Group.Option Value"))
{
}
void ConfigCommand::startCommand()
{
int numArgs = args().size();
QString group, option;
GeneralConfig* cfg = nullptr;
QVariant value;
if (numArgs > 1) {
const QString& groupOption = args().at(1);
int dotPos = groupOption.indexOf(QLatin1Char('.'));
if (dotPos > 0) {
group = groupOption.left(dotPos);
option = groupOption.mid(dotPos + 1);
} else {
group = groupOption;
}
cfg = getConfig(group);
if (!cfg) {
setError(tr("%1 does not exist").arg(group));
return;
}
if (!option.isNull()) {
value = cfg->property(option.toLatin1());
if (!value.isValid()) {
setError(tr("%1 does not exist").arg(option));
return;
}
}
}
if (numArgs > 2) {
const QMetaObject* metaObj = nullptr;
int propIdx = -1;
if (!option.isNull() && (metaObj = cfg->metaObject()) != nullptr &&
(propIdx = metaObj->indexOfProperty(option.toLatin1())) >= 0) {
#if QT_VERSION >= 0x060000
auto propType = metaObj->property(propIdx).typeId();
if (propType == QMetaType::QStringList) {
value = QVariant(args().mid(2));
} else if (propType == QMetaType::Int) {
value = configIntFromEnumName(group, option, args().at(2));
} else if (propType == QMetaType::Bool) {
value = QVariant(args().at(2)).toBool();
} else {
value = args().at(2);
}
if (value.typeId() == propType) {
cfg->setProperty(option.toLatin1(), value);
cli()->app()->applyChangedConfiguration();
// The value is read back and will be displayed.
value = cfg->property(option.toLatin1());
} else {
setError(tr("Invalid value %1").arg(value.toString()));
return;
}
#else
QVariant::Type propType = metaObj->property(propIdx).type();
if (propType == QVariant::StringList) {
value = QVariant(args().mid(2));
} else if (propType == QVariant::Int) {
value = configIntFromEnumName(group, option, args().at(2));
} else if (propType == QVariant::Bool) {
value = QVariant(args().at(2)).toBool();
} else {
value = args().at(2);
}
if (value.type() == propType) {
cfg->setProperty(option.toLatin1(), value);
cli()->app()->applyChangedConfiguration();
// The value is read back and will be displayed.
value = cfg->property(option.toLatin1());
} else {
setError(tr("Invalid value %1").arg(value.toString()));
return;
}
#endif
}
}
if (numArgs > 1) {
if (option.isNull()) {
if (auto metaObj = cfg->metaObject()) {
QStringList propertyNames;
for (int i = 0; i < metaObj->propertyCount(); ++i) {
QString propertyName = QString::fromLatin1(metaObj->property(i).name());
if (!excludedConfigPropertyNames.contains(propertyName)) {
propertyNames.append(propertyName);
}
}
cli()->writeResult(propertyNames);
}
} else {
#if QT_VERSION >= 0x060000
if (value.typeId() == QMetaType::QStringList) {
cli()->writeResult(value.toStringList());
} else if (value.typeId() == QMetaType::QVariantMap) {
cli()->writeResult(value.toMap());
} else if (value.typeId() == QMetaType::Int) {
value = configIntToEnumName(group, option, value);
cli()->writeResult(value.toString());
} else if (value.typeId() == QMetaType::Bool) {
cli()->writeResult(value.toBool());
} else {
cli()->writeResult(value.toString());
}
#else
if (value.type() == QVariant::StringList) {
cli()->writeResult(value.toStringList());
} else if (value.type() == QVariant::Map) {
cli()->writeResult(value.toMap());
} else if (value.type() == QVariant::Int) {
value = configIntToEnumName(group, option, value);
cli()->writeResult(value.toString());
} else if (value.type() == QVariant::Bool) {
cli()->writeResult(value.toBool());
} else {
cli()->writeResult(value.toString());
}
#endif
}
} else {
cli()->writeResult(configNames);
}
}