include(CMakeFindDependencyMacro)
# TODO find_dependency with components is nonstandard
-find_dependency(Qt5Network)
+find_dependency(Qt6Network)
+find_dependency(Qt6StateMachine)
+find_dependency(Qt6Qml)
include("${CMAKE_CURRENT_LIST_DIR}/@CONFIG_NAME@Targets.cmake")
-cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
+cmake_minimum_required(VERSION 4.0 FATAL_ERROR)
include(FeatureSummary)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
option(WITH_CLIENT_DEBUG "Enable client debug logs" OFF)
add_feature_info(ClientDebug WITH_CLIENT_DEBUG "Print extra logs for debug purposes")
-set(QT_MIN_VERSION "5.8.0")
-find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
+set(QT_MIN_VERSION "6.10.0")
+find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Core
Network
- Script
+ StateMachine
+ # For QJSEngine
+ Qml
)
set(AniDBUdpClient_PUBLIC_LIBS
- Qt5::Core
- Qt5::Network
+ Qt6::Core
+ Qt6::Network
+ Qt6::StateMachine
+ Qt6::Qml
)
set(AniDBUdpClient_SOURCES
)
if(WITH_RENAMEPARSER)
+ find_package(AucQJSEngineFunction CONFIG REQUIRED)
+ set(AniDBUdpClient_LIBS ${AniDBUdpClient_LIBS} AucQJSEngineFunction::AucQJSEngineFunction)
+
set(AniDBUdpClient_RENAMEPARSER_HEADERS
renameparser/renameengine.h
renameparser/functions.h
include/RenameParser/RenameEngine
)
- set(AniDBUdpClient_LIBS
- ${AniDBUdpClient_LIBS}
- Qt5::Script
- )
-
install(FILES
${AniDBUdpClient_RENAMEPARSER_HEADERS}
${AniDBUdpClient_RENAMEPARSER_CONV_HEADERS}
if(WITH_ENCRYPTION)
- set(QCA_NAME "Qca-qt5")
+ set(QCA_NAME "Qca-qt6")
find_package(${QCA_NAME} CONFIG REQUIRED)
if (${QCA_NAME}_FOUND)
# TODO Runtime plugins are required
void AnimeReply::readReplyData(const QString &reply)
{
QString d = reply.mid(reply.indexOf('\n')).trimmed();
- QStringList parts = d.split('|', QString::KeepEmptyParts);
+ QStringList parts = d.split('|', Qt::KeepEmptyParts);
if (command().amask() == 0)
{
QDateTime AnimeReply::dateRecordUpdated() const
{
- return QDateTime::fromTime_t(animeFlagData.value(AnimeFlag::DateRecordUpdated).toUInt());
+ return QDateTime::fromSecsSinceEpoch(
+ animeFlagData.value(AnimeFlag::DateRecordUpdated).toUInt(),
+ QTimeZone::UTC);
}
QList<int> AnimeReply::tagWeightList() const
QDateTime AnimeReply::endDate() const
{
- return QDateTime::fromTime_t(animeFlagData.value(AnimeFlag::EndDate).toUInt());
+ return QDateTime::fromSecsSinceEpoch(
+ animeFlagData.value(AnimeFlag::EndDate).toUInt(), QTimeZone::UTC);
}
QDateTime AnimeReply::airDate() const
{
- return QDateTime::fromTime_t(animeFlagData.value(AnimeFlag::AirDate).toUInt());
+ return QDateTime::fromSecsSinceEpoch(
+ animeFlagData.value(AnimeFlag::AirDate).toUInt(), QTimeZone::UTC);
}
int AnimeReply::specialEpCount() const
#include <QState>
#include <QCoreApplication>
-#include <QUdpSocket>
+#include <QRegularExpression>
#include <QTimer>
-#include <QRegExp>
+#include <QUdpSocket>
#include "rawcommand.h"
#include "logoutcommand.h"
// Check if it is a 6xx error.
{
- QRegExp rx("(?:50[34]|555|598|6[0-9]{2}) ");
- if (rx.exactMatch(tmp.mid(0, 4)))
+ static QRegularExpression rx("(?:50[34]|555|598|6[0-9]{2}) ");
+ auto match = rx.match(tmp.mid(0, 4));
+ if (match.hasMatch())
{
int replyCode = tmp.mid(0, 3).toInt();
switch (replyCode)
#include "clientinterface.h"
+#include <QMetaType>
+#include <QRandomGenerator>
+
namespace AniDBUdpClient {
ClientInterface::ClientInterface(QObject *parent) :
QByteArray result(len, '-');
while (len--)
- result[len] = chars[qrand() % numChars];
+ result[len] = chars[QRandomGenerator::global()->bounded(numChars)];
#ifdef ANIDBUDPCLIENT_CLIENT_MISC_DEBUG
qDebug() << QString("Generated id %1").arg(result.constData());
QString result = cmd;
for (QVariantMap::const_iterator it = args.constBegin(); it != args.constEnd(); ++it)
{
- if (!it.value().canConvert(QVariant::String))
+ if (!it.value().canConvert<QString>())
{
qWarning("Passed value cannot be converted to string!");
continue;
// The string version of bool is "true" or "false", but the API expects 1 or 0
QString value;
- if (it.value().type() == QVariant::Bool)
+ if (it.value().typeId() == QMetaType::Bool)
{
value = it.value().toBool() ? "1" : "0";
}
{
case EPISODE:
{
- QStringList parts = reply.mid(reply.indexOf("\n")).split('|', QString::KeepEmptyParts);
+ QStringList parts =
+ reply.mid(reply.indexOf("\n")).split('|', Qt::KeepEmptyParts);
if (parts.count() < 10)
{
signalReplyReady(false);
m_titleEnglish = parts[6];
m_titleRomaji = parts[7];
m_titleKanji = parts[8];
- m_airDate = QDateTime::fromTime_t(parts[9].toUInt(&ok, 10));
+ m_airDate = QDateTime::fromSecsSinceEpoch(parts[9].toUInt(&ok, 10),
+ QTimeZone::UTC);
if (parts.size() > 11)
{
m_typeAsInt = parts[10].toInt(&ok, 10);
#include "file.h"
-#include <QRegExp>
+#include <QRegularExpression>
#include "client.h"
#include "hash.h"
#include "filerenamedelegate.h"
File *File::fromEd2k(const QString &ed2k)
{
- QRegExp rx("^ed2k://\\|file\\|[^|]+\\|([0-9]+)\\|([a-z0-9]{32})\\|", Qt::CaseInsensitive, QRegExp::RegExp2);
+ static QRegularExpression rx(
+ "^ed2k://\\|file\\|[^|]+\\|([0-9]+)\\|([a-z0-9]{32})\\|",
+ QRegularExpression::CaseInsensitiveOption);
- if (rx.indexIn(ed2k) == -1)
+ auto match = rx.match(ed2k);
+ if (!match.hasMatch())
return 0;
bool ok = false;
- qint64 size = rx.cap(1).toLongLong(&ok);
+ qint64 size = match.captured(1).toLongLong(&ok);
if (!ok)
return 0;
- QByteArray hash = rx.cap(2).toLower().toLatin1();
+ QByteArray hash = match.captured(2).toLower().toLatin1();
File *ret = new File();
ret->m_size = size;
.arg(fileReply->value(FileFlag::FileType).toString());
}
newFileName.replace('"', "'");
- newFileName.replace(QRegExp("[\\/]"), "-");
- newFileName.replace(QRegExp("[\\/:*?\"<>|]"), "");
+ static QRegularExpression rx1("[\\/]");
+ newFileName.replace(rx1, "-");
+ static QRegularExpression rx2("[\\/:*?\"<>|]");
+ newFileName.replace(rx2, "");
#ifdef ANIDBUDPCLIENT_RENAME_DEBUG
qDebug() << "New file name:" << newFileName;
#endif
void FileReply::readReplyData(const QString &reply)
{
QString d = reply.mid(reply.indexOf('\n')).trimmed();
- QList<QString> parts = d.split('|', QString::KeepEmptyParts);
+ QList<QString> parts = d.split('|', Qt::KeepEmptyParts);
m_fid = parts[0].toInt();
if (command().fmask() == 0 && command().amask() == 0)
QDateTime FileReply::dateAidRecordUpdated() const
{
- return QDateTime::fromTime_t(fileAnimeFlagData.value(FileAnimeFlag::DateAidRecordUpdated).toUInt());
+ return QDateTime::fromSecsSinceEpoch(
+ fileAnimeFlagData.value(FileAnimeFlag::DateAidRecordUpdated).toUInt(),
+ QTimeZone::UTC);
}
QString FileReply::groupShortName() const
bool ok;
uint timestamp = fileFlagData.value(FileFlag::MyListViewDate).toUInt(&ok);
if (ok && timestamp)
- return QDateTime::fromTime_t(timestamp);
+ return QDateTime::fromSecsSinceEpoch(timestamp, QTimeZone::UTC);
return QDateTime();
}
if (!m_viewDate.isNull())
{
- cmd.second["viewdate"] = m_viewDate.toTime_t();
+ cmd.second["viewdate"] = m_viewDate.toSecsSinceEpoch();
}
if (!m_source.isEmpty())
{
case MYLIST:
{
- QStringList parts = reply.mid(reply.indexOf("\n")).split('|', QString::KeepEmptyParts);
+ QStringList parts = reply.mid(reply.indexOf("\n")).split('|', Qt::KeepEmptyParts);
if (parts.count() < 12)
{
signalReplyReady(false);
m_eid = parts[2].toInt(&ok, 10);
m_aid = parts[3].toInt(&ok, 10);
m_gid = parts[4].toInt(&ok, 10);
- m_date = QDateTime::fromTime_t(parts[5].toUInt(&ok, 10));
+ m_date = QDateTime::fromSecsSinceEpoch(parts[5].toUInt(&ok, 10),
+ QTimeZone::UTC);
m_state = State(parts[6].toInt(&ok, 10));
- m_viewDate = QDateTime::fromTime_t(parts[7].toUInt(&ok, 10));
+ m_viewDate = QDateTime::fromSecsSinceEpoch(parts[7].toUInt(&ok, 10),
+ QTimeZone::UTC);
m_storage = parts[8];
m_source = parts[9];
m_other = parts[10];
break;
case MULTIPLE_MYLIST_ENTRIES:
{
- m_multipleEntries = reply.mid(reply.indexOf("\n")).split('|', QString::KeepEmptyParts);
+ m_multipleEntries = reply.mid(reply.indexOf("\n")).split('|', Qt::KeepEmptyParts);
signalReplyReady(true);
}
break;
bool Lexer::next()
{
- m_pos = rx.indexIn(m_data, m_pos);
+ m_last_match = rx.match(m_data, m_pos);
+ m_pos = m_last_match.capturedStart();
- if (m_pos == -1)
+ if (!m_last_match.hasMatch())
{
m_type = 0;
return false;
}
#ifdef PARSER_DEBUG
-QString m = rx.cap();
-qDebug() << "TOKEN=" << m;
+ QString m = rx.cap();
+ qDebug() << "TOKEN=" << m;
#endif
- m_pos += rx.matchedLength();
- m_column += rx.matchedLength();
+ m_pos += m_last_match.capturedLength();
+ m_column += m_last_match.capturedLength();
- if (rx.cap() == "\n" || rx.cap()[0] == QChar('#') || rx.cap().left(2) == "//")
+ if (m_last_match.captured() == "\n" ||
+ m_last_match.captured()[0] == QChar('#') ||
+ m_last_match.captured().left(2) == "//")
{
++m_line;
m_column = 1;
return true;
}
- if (tokenMap.contains(rx.cap()))
+ if (tokenMap.contains(m_last_match.captured()))
{
- m_type = tokenMap.value(rx.cap());
+ m_type = tokenMap.value(m_last_match.captured());
}
- else if (rx.cap()[0] == QChar('"') || rx.cap()[0] == QChar('\''))
+ else if (m_last_match.captured()[0] == QChar('"') ||
+ m_last_match.captured()[0] == QChar('\''))
{
m_type = RenameGrammar::STRING;
}
else
{
bool ok;
- rx.cap().toDouble(&ok);
+ m_last_match.captured().toDouble(&ok);
if (ok)
m_type = RenameGrammar::NUMBER;
QString Lexer::value() const
{
if (m_type == RenameGrammar::STRING)
- return rx.cap().mid(1, rx.cap().length() - 2);
- return rx.cap();
+ return m_last_match.captured().mid(1, m_last_match.captured().length() -
+ 2);
+ return m_last_match.captured();
}
int Lexer::type() const
const char *Lexer::regexp = ":=|=|%|\\?|:|\\$|,|\\(|\\)|\\[|\\]|\\{|\\}|\\n|else|if|(:?//|#)[^\\n]*\\n|[^\"\\[\\]:= {}(),%$?\\n]+|\"[^\"]*\"|'[^']*'";
-QRegExp Lexer::rx(Lexer::regexp);
+QRegularExpression Lexer::rx(Lexer::regexp);
QMap<QString, int> Lexer::tokenMap;
bool Lexer::staticInitialised = false;
#define LEXER_H
#include <QMap>
-#include <QRegExp>
+#include <QRegularExpression>
+#include <QRegularExpressionMatch>
#include "renameparser.h"
int m_column;
int m_pos;
- static QRegExp rx;
+ QRegularExpressionMatch m_last_match;
+ static QRegularExpression rx;
static const char *regexp;
static QMap<QString, int> tokenMap;
#include "parser.h"
-#include <QScriptValueIterator>
+#include "../renameengine.h"
+#include <AucQJSEngineFunction/aucqjsenginefunction.h>
-namespace RenameParser {
-namespace ECMAScript {
+#include <QJSValueIterator>
+#include <utility>
-QScriptValue RenameEngine2QtScriptHelper(QScriptContext *context, QScriptEngine *engine, void *arg)
+namespace RenameParser {
+namespace ECMAScript
{
- Q_UNUSED(engine);
-
- RenameFunction func = (RenameFunction) arg;
-
- QStringList args;
- for (int i = 0; i < context->argumentCount(); ++i)
- args << context->argument(i).toString();
+QJSValue RenameEngineFunctionWrapperHelper(RenameParser::RenameFunction func,
+ const QJSValueList &args)
+{
+ QStringList strArgs;
+ for (const auto &arg : args)
+ strArgs << arg.toString();
- return QScriptValue(func(args));
+ return QJSValue{func(strArgs)};
}
Parser::Parser() : AbstractParser()
m_line = 0;
m_column = 0;
- QScriptSyntaxCheckResult checkResult = engine.checkSyntax(string);
+ QJSEngine engine;
- if (checkResult.state() == QScriptSyntaxCheckResult::Valid)
- {
- program = string;
- return true;
- }
- m_error = checkResult.errorMessage();
- m_line = checkResult.errorLineNumber();
- m_column = checkResult.errorColumnNumber();
+ QJSValue result = engine.evaluate(string, QStringLiteral("<input>"), 1);
- return false;
+ if (result.isError()) {
+ m_line = result.property("lineNumber").toInt();
+ m_column = 0;
+ m_error =
+ QStringLiteral("%1 at line %2").arg(result.toString()).arg(m_line);
+ return false;
+ }
+ program = string;
+ return true;
}
QString Parser::evaluate(Environment &env) const
{
- QScriptContext *fct = engine.pushContext();
-
- foreach (const QString &funcName, RenameEngine::registeredFunctions())
+ QJSEngine engine;
+ const auto functionList = RenameEngine::registeredFunctions();
+ for (const QString &funcName : std::as_const(functionList))
{
- fct->activationObject().setProperty(funcName,
- engine.newFunction(
- &RenameEngine2QtScriptHelper, (void *) RenameEngine::function(funcName)));
+ RenameFunction func = RenameEngine::function(funcName);
+ AucQJSEngineFunction::registerFunction(
+ engine, funcName, [func](const QJSValueList &args) -> QJSValue
+ { return RenameEngineFunctionWrapperHelper(func, args); });
}
-
- QScriptContext *ct = engine.pushContext();
-
- for (Environment::const_iterator i = env.constBegin(); i != env.constEnd(); ++i)
+ for (auto it = env.constBegin(); it != env.constEnd(); ++it)
{
- ct->activationObject().setProperty(i.key(), i.value());
+ engine.globalObject().setProperty(it.key(), it.value());
}
QString ret = engine.evaluate(program).toString();
- QScriptValueIterator it(ct->activationObject());
-
+ QJSValueIterator it(engine.globalObject());
while (it.hasNext())
{
it.next();
+ if (!env.contains(it.name()))
+ continue;
env[it.name()] = it.value().toString();
}
-
- engine.popContext();
- engine.popContext();
-
return ret;
}
return m_column;
}
-}} // namespace
+} // namespace ECMAScript
+} // namespace RenameParser
#ifndef ECMASCRIPTPARSER_H
#define ECMASCRIPTPARSER_H
-#include <QScriptEngine>
-#include <QScriptSyntaxCheckResult>
#include "../abstractparser.h"
-#include "../renameengine.h"
+#include <QJSEngine>
namespace RenameParser {
namespace ECMAScript {
int column() const;
private:
- mutable QScriptEngine engine;
- QScriptProgram program;
+ QString program;
QString m_error;
int m_line;
int m_column;
};
-}} // namespace
+} // namespace ECMAScript
+} // namespace RenameParser
#endif // ECMASCRIPTPARSER_H
#ifndef AST_H
#define AST_H
-#include <QString>
+#include <QList>
#include <QSharedPointer>
+#include <QString>
namespace RenameParser {
namespace AST
#include "functions.h"
+#include <QRegularExpression>
+
namespace RenameParser {
namespace RenameFunctions {
if (args.count() != 3)
return "";
QString ret = args[0];
- return ret.replace(QRegExp(args[1], Qt::CaseSensitive, QRegExp::RegExp2), args[2]);
+ return ret.replace(
+ QRegularExpression(args[1], QRegularExpression::CaseInsensitiveOption),
+ args[2]);
}
QString match(const QStringList &args)
{
if (args.count() != 2)
return "";
- return QRegExp(args[1], Qt::CaseSensitive, QRegExp::RegExp2).indexIn(args[0]) != -1 ? "1" : "";
+ return QRegularExpression(args[1],
+ QRegularExpression::CaseInsensitiveOption)
+ .match(args[0])
+ .hasMatch()
+ ? "1"
+ : "";
}
QString uc(const QStringList &args)
{
if (args.isEmpty())
return "";
- return QString(args.at(0)).replace(QRegExp("[/\\:*\"?<>|\\r\\n]"), QString(""));
+ static QRegularExpression rx("[/\\:*\"?<>|\\r\\n]");
+ return QString(args.at(0)).replace(rx, QString(""));
}
} // namespace RenameFunctions
case UPDATED:
{
QString d = reply.mid(reply.indexOf('\n')).trimmed();
- QStringList parts = d.split('|', QString::KeepEmptyParts);
+ QStringList parts = d.split('|', Qt::KeepEmptyParts);
if (parts.count() < 4)
{
bool VoteReply::readReplyData(const QString &reply)
{
QString d = reply.mid(reply.indexOf('\n')).trimmed();
- QStringList parts = d.split('|', QString::KeepEmptyParts);
+ QStringList parts = d.split('|', Qt::KeepEmptyParts);
if (parts.count() < 4)
{